From accadc7759ca6e3690749417ef7d26d443bea5f7 Mon Sep 17 00:00:00 2001 From: Anthony Pesch Date: Wed, 7 Aug 2013 13:55:38 -0700 Subject: [PATCH 001/112] - select shouldn't return an error when the socket is valid, but disconnected - recv should return 0 once the socket has disconnected - send should set ENOTCONN once closed, and EAGAIN while connecting - updated tests to use O_NONBLOCK and better conform to coding standards --- src/library.js | 62 +++--- tests/runner.py | 63 ++++--- tests/websockets.c | 118 ++++++------ tests/websockets_bi.c | 116 ++++++------ tests/websockets_bi_bigdata.c | 117 ++++++------ tests/websockets_bi_listener.c | 151 --------------- tests/websockets_bi_side.c | 103 +++++----- tests/websockets_bi_side_bigdata.c | 89 +++++---- tests/websockets_gethostbyname.c | 114 ++++++------ tests/websockets_partial.c | 112 ++++++----- tests/websockets_select.c | 101 +++++----- ...bsockets_select_server_closes_connection.c | 124 ++++++------ ...ckets_select_server_closes_connection_rw.c | 176 +++++++++--------- 13 files changed, 670 insertions(+), 776 deletions(-) delete mode 100644 tests/websockets_bi_listener.c diff --git a/src/library.js b/src/library.js index f48220bbd98c9..0a37f2123fce8 100644 --- a/src/library.js +++ b/src/library.js @@ -9439,17 +9439,30 @@ LibraryManager.library = { return -1; } - return 0; + // always "fail" in non-blocking mode + ___setErrNo(ERRNO_CODES.EINPROGRESS); + return -1; }, recv__deps: ['$FS'], recv: function(fd, buf, len, flags) { var info = FS.getStream(fd); - if (!info) return -1; - if (!info.hasData()) { - ___setErrNo(ERRNO_CODES.EAGAIN); // no data, and all sockets are nonblocking, so this is the right behavior + if (!info) { + ___setErrNo(ERRNO_CODES.EBADF); return -1; } +#if SOCKET_WEBRTC == 0 + if (!info.hasData()) { + if (info.socket.readyState === WebSocket.CLOSING || info.socket.readyState === WebSocket.CLOSED) { + // socket has closed + return 0; + } else { + // else, our socket is in a valid state but truly has nothing available + ___setErrNo(ERRNO_CODES.EAGAIN); + return -1; + } + } +#endif var buffer = info.inQueue.shift(); #if SOCKET_DEBUG Module.print('recv: ' + [Array.prototype.slice.call(buffer)]); @@ -9471,7 +9484,19 @@ LibraryManager.library = { send__deps: ['$FS'], send: function(fd, buf, len, flags) { var info = FS.getStream(fd); - if (!info) return -1; + if (!info) { + ___setErrNo(ERRNO_CODES.EBADF); + return -1; + } +#if SOCKET_WEBRTC == 0 + if (info.socket.readyState === WebSocket.CLOSING || info.socket.readyState === WebSocket.CLOSED) { + ___setErrNo(ERRNO_CODES.ENOTCONN); + return -1; + } else if (info.socket.readyState === WebSocket.CONNECTING) { + ___setErrNo(ERRNO_CODES.EAGAIN); + return -1; + } +#endif info.sender(HEAPU8.subarray(buf, buf+len)); return len; }, @@ -9607,7 +9632,8 @@ LibraryManager.library = { bind__deps: ['connect'], bind: function(fd, addr, addrlen) { - return _connect(fd, addr, addrlen); + _connect(fd, addr, addrlen); + return 0; }, listen: function(fd, backlog) { @@ -9640,24 +9666,12 @@ LibraryManager.library = { var errorCondition = 0; function canRead(info) { - // make sure hasData exists. - // we do create it when the socket is connected, - // but other implementations may create it lazily - if ((info.socket.readyState == WebSocket.CLOSING || info.socket.readyState == WebSocket.CLOSED) && info.inQueue.length == 0) { - errorCondition = -1; - return false; - } - return info.hasData && info.hasData(); + return (info.hasData && info.hasData()) || + info.socket.readyState == WebSocket.CLOSING || // let recv return 0 once closed + info.socket.readyState == WebSocket.CLOSED; } function canWrite(info) { - // make sure socket exists. - // we do create it when the socket is connected, - // but other implementations may create it lazily - if ((info.socket.readyState == WebSocket.CLOSING || info.socket.readyState == WebSocket.CLOSED)) { - errorCondition = -1; - return false; - } return info.socket && (info.socket.readyState == info.socket.OPEN); } @@ -9676,7 +9690,11 @@ LibraryManager.library = { if (int_ & mask) { // index is in the set, check if it is ready for read var info = FS.getStream(fd); - if (info && can(info)) { + if (!info) { + ___setErrNo(ERRNO_CODES.EBADF); + return -1; + } + if (can(info)) { // set bit fd < 32 ? (dstLow = dstLow | mask) : (dstHigh = dstHigh | mask); bitsSet++; diff --git a/tests/runner.py b/tests/runner.py index b866cc087534b..2bd26eb72ff93 100755 --- a/tests/runner.py +++ b/tests/runner.py @@ -12330,6 +12330,24 @@ def run_in_other_browser(url): 'browser.test_freealut' ] + if 'sockets' in sys.argv: + print + print 'Running the browser socket tests.' + print + i = sys.argv.index('sockets') + sys.argv = sys.argv[:i] + sys.argv[i+1:] + i = sys.argv.index('browser') + sys.argv = sys.argv[:i] + sys.argv[i+1:] + sys.argv += [ + 'browser.test_sockets_bi', + 'browser.test_sockets_gethostbyname', + 'browser.test_sockets_bi_bigdata', + 'browser.test_sockets_select_server_down', + 'browser.test_sockets_select_server_closes_connection', + 'browser.test_sockets_select_server_closes_connection_rw', + 'browser.test_enet' + ] + # Run a server and a web page. When a test runs, we tell the server about it, # which tells the web page, which then opens a window with the test. Doing # it this way then allows the page to close() itself when done. @@ -13930,14 +13948,14 @@ def __exit__(self, *args, **kwargs): # always run these tests last # make sure to use different ports in each one because it takes a while for the processes to be cleaned up - def test_websockets(self): + def test_sockets(self): try: with self.WebsockHarness(8990): - self.btest('websockets.c', expected='571') + self.btest('websockets.c', expected='571', args=['-DSOCKK=8991']) finally: self.clean_pids() - def test_websockets_partial(self): + def test_sockets_partial(self): def partial(q): import socket @@ -13960,7 +13978,7 @@ def partial(q): try: with self.WebsockHarness(8990, partial): - self.btest('websockets_partial.c', expected='165') + self.btest('websockets_partial.c', expected='165', args=['-DSOCKK=8991']) finally: self.clean_pids() @@ -13972,35 +13990,26 @@ def relay_server(q): proc.communicate() return relay_server - def test_websockets_bi(self): + def test_sockets_bi(self): for datagram in [0,1]: for fileops in [0,1]: try: print >> sys.stderr, 'test_websocket_bi datagram %d, fileops %d' % (datagram, fileops) - with self.WebsockHarness(8992, self.make_relay_server(8992, 8994)): - with self.WebsockHarness(8994, no_server=True): - Popen([PYTHON, EMCC, path_from_root('tests', 'websockets_bi_side.c'), '-o', 'side.html', '-DSOCKK=8995', '-DTEST_DGRAM=%d' % datagram]).communicate() - self.btest('websockets_bi.c', expected='2499', args=['-DSOCKK=8993', '-DTEST_DGRAM=%d' % datagram, '-DTEST_FILE_OPS=%s' % fileops]) + with self.WebsockHarness(6992, self.make_relay_server(6992, 6994)): + with self.WebsockHarness(6994, no_server=True): + Popen([PYTHON, EMCC, path_from_root('tests', 'websockets_bi_side.c'), '-o', 'side.html', '-DSOCKK=6995', '-DTEST_DGRAM=%d' % datagram]).communicate() + self.btest('websockets_bi.c', expected='2499', args=['-DSOCKK=6993', '-DTEST_DGRAM=%d' % datagram, '-DTEST_FILE_OPS=%s' % fileops, '-DEMBED_SIDE']) finally: self.clean_pids() - def test_websockets_bi_listen(self): - try: - with self.WebsockHarness(6992, self.make_relay_server(6992, 6994)): - with self.WebsockHarness(6994, no_server=True): - Popen([PYTHON, EMCC, path_from_root('tests', 'websockets_bi_side.c'), '-o', 'side.html', '-DSOCKK=6995']).communicate() - self.btest('websockets_bi_listener.c', expected='2499', args=['-DSOCKK=6993']) - finally: - self.clean_pids() - - def test_websockets_gethostbyname(self): + def test_sockets_gethostbyname(self): try: with self.WebsockHarness(7000): - self.btest('websockets_gethostbyname.c', expected='571', args=['-O2']) + self.btest('websockets_gethostbyname.c', expected='571', args=['-O2', '-DSOCKK=7001']) finally: self.clean_pids() - def test_websockets_bi_bigdata(self): + def test_sockets_bi_bigdata(self): try: with self.WebsockHarness(3992, self.make_relay_server(3992, 3994)): with self.WebsockHarness(3994, no_server=True): @@ -14009,7 +14018,7 @@ def test_websockets_bi_bigdata(self): finally: self.clean_pids() - def test_websockets_select_server_down(self): + def test_sockets_select_server_down(self): def closedServer(q): import socket @@ -14018,11 +14027,11 @@ def closedServer(q): ssock.bind(("127.0.0.1", 8994)) try: with self.WebsockHarness(8994, closedServer): - self.btest('websockets_select.c', expected='266') + self.btest('websockets_select.c', expected='266', args=['-DSOCKK=8995']) finally: self.clean_pids() - def test_websockets_select_server_closes_connection(self): + def test_sockets_select_server_closes_connection(self): def closingServer(q): import socket @@ -14038,11 +14047,11 @@ def closingServer(q): try: with self.WebsockHarness(8994, closingServer): - self.btest('websockets_select_server_closes_connection.c', expected='266') + self.btest('websockets_select_server_closes_connection.c', expected='266', args=['-DSOCKK=8995']) finally: self.clean_pids() - def test_websockets_select_server_closes_connection_rw(self): + def test_sockets_select_server_closes_connection_rw(self): def closingServer_rw(q): import socket @@ -14070,7 +14079,7 @@ def closingServer_rw(q): try: with self.WebsockHarness(8998, closingServer_rw): - self.btest('websockets_select_server_closes_connection_rw.c', expected='266') + self.btest('websockets_select_server_closes_connection_rw.c', expected='266', args=['-DSOCKK=8999']) finally: self.clean_pids() diff --git a/tests/websockets.c b/tests/websockets.c index 8882f5ba25a1e..8845ef43b2afa 100644 --- a/tests/websockets.c +++ b/tests/websockets.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #if EMSCRIPTEN @@ -15,12 +16,18 @@ #define EXPECTED_BYTES 5 -int SocketFD; - +int sockfd; int not_always_data = 0; -unsigned int get_all_buf(int sock, char* output, unsigned int maxsize) -{ +void finish(int result) { + close(sockfd); +#if EMSCRIPTEN + REPORT_RESULT(); +#endif + exit(result); +} + +unsigned int get_all_buf(int sock, char* output, unsigned int maxsize) { // select check for IO fd_set sett; FD_ZERO(&sett); @@ -35,6 +42,7 @@ unsigned int get_all_buf(int sock, char* output, unsigned int maxsize) int bytes; if (ioctl(sock, FIONREAD, &bytes) || bytes == 0) { not_always_data = 1; + printf("ioctl says 0, FD_ISSET says %ld\n", FD_ISSET(sock, &sett)); assert(FD_ISSET(sock, &sett) == 0); return 0; } @@ -47,28 +55,42 @@ unsigned int get_all_buf(int sock, char* output, unsigned int maxsize) unsigned int offset = 0; while((errno = 0, (n = recv(sock, buffer, sizeof(buffer), 0))>0) || errno == EINTR) { - if(n>0) - { - if (((unsigned int) n)+offset > maxsize) { fprintf(stderr, "too much data!"); exit(EXIT_FAILURE); } + if(n > 0) { + if (((unsigned int) n)+offset > maxsize) { + fprintf(stderr, "too much data!"); + finish(EXIT_FAILURE); + } memcpy(output+offset, buffer, n); offset += n; } } - if(n < 0 && errno != EAGAIN && errno != EWOULDBLOCK) { + if (n < 0 && errno != EAGAIN && errno != EWOULDBLOCK) { fprintf(stderr, "error in get_all_buf! %d", errno); - exit(EXIT_FAILURE); + finish(EXIT_FAILURE); } return offset; } -int done = 0; - void iter(void *arg) { - /* perform read write operations ... */ static char out[1024*2]; static int pos = 0; - int n = get_all_buf(SocketFD, out+pos, 1024-pos); + fd_set fdr; + int res; + + // make sure that sockfd is ready to read + FD_ZERO(&fdr); + FD_SET(sockfd, &fdr); + res = select(64, &fdr, NULL, NULL, NULL); + if (res == -1) { + perror("select failed"); + finish(EXIT_FAILURE); + } else if (!FD_ISSET(sockfd, &fdr)) { + return; + } + + // perform read write operations ... + int n = get_all_buf(sockfd, out+pos, 1024-pos); if (n) printf("read! %d\n", n); pos += n; if (pos >= EXPECTED_BYTES) { @@ -78,70 +100,44 @@ void iter(void *arg) { sum += out[i]; } - shutdown(SocketFD, SHUT_RDWR); - - close(SocketFD); + shutdown(sockfd, SHUT_RDWR); - done = 1; + close(sockfd); printf("sum: %d\n", sum); - -#if EMSCRIPTEN - //assert(not_always_data == 1); - - int result = sum; - REPORT_RESULT(); -#endif + finish(sum); } } -int main(void) -{ - struct sockaddr_in stSockAddr; - int Res; - SocketFD = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); +void main() { + struct sockaddr_in addr; + int res; - if (-1 == SocketFD) - { + sockfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + if (sockfd == -1) { perror("cannot create socket"); - exit(EXIT_FAILURE); + finish(EXIT_FAILURE); } - - memset(&stSockAddr, 0, sizeof(stSockAddr)); - - stSockAddr.sin_family = AF_INET; - stSockAddr.sin_port = htons( -#if EMSCRIPTEN - 8991 -#else - 8990 -#endif - ); - Res = inet_pton(AF_INET, "127.0.0.1", &stSockAddr.sin_addr); - - if (0 > Res) { - perror("error: first parameter is not a valid address family"); - close(SocketFD); - exit(EXIT_FAILURE); - } else if (0 == Res) { - perror("char string (second parameter does not contain valid ipaddress)"); - close(SocketFD); - exit(EXIT_FAILURE); + fcntl(sockfd, F_SETFL, O_NONBLOCK); + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(SOCKK); + if (inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr) != 1) { + perror("inet_pton failed"); + finish(EXIT_FAILURE); } - - if (-1 == connect(SocketFD, (struct sockaddr *)&stSockAddr, sizeof(stSockAddr))) { + + res = connect(sockfd, (struct sockaddr *)&addr, sizeof(addr)); + if (res == -1 && errno != EINPROGRESS) { perror("connect failed"); - close(SocketFD); - exit(EXIT_FAILURE); - + finish(EXIT_FAILURE); } #if EMSCRIPTEN emscripten_set_main_loop(iter, 0, 0); #else - while (!done) iter(NULL); + while (1) iter(NULL); #endif - - return EXIT_SUCCESS; } diff --git a/tests/websockets_bi.c b/tests/websockets_bi.c index fb60177b56f9f..e19f7fe88a99b 100644 --- a/tests/websockets_bi.c +++ b/tests/websockets_bi.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #if EMSCRIPTEN #include @@ -14,14 +15,17 @@ #define EXPECTED_BYTES 28 -#ifndef SOCKK -#define SOCKK 8992 -#endif +int sockfd; -int SocketFD; +void finish(int result) { + close(sockfd); +#if EMSCRIPTEN + REPORT_RESULT(); +#endif + exit(result); +} -unsigned int get_all_buf(int sock, char* output, unsigned int maxsize) -{ +unsigned int get_all_buf(int sock, char* output, unsigned int maxsize) { int bytes; if (ioctl(sock, FIONREAD, &bytes)) return 0; if (bytes == 0) return 0; @@ -35,9 +39,11 @@ unsigned int get_all_buf(int sock, char* output, unsigned int maxsize) while((errno = 0, (n = recv(sock, buffer, sizeof(buffer), 0))>0) || #endif errno == EINTR) { - if(n>0) - { - if (((unsigned int) n)+offset > maxsize) { fprintf(stderr, "too much data!"); exit(EXIT_FAILURE); } + if(n > 0) { + if (((unsigned int) n)+offset > maxsize) { + fprintf(stderr, "too much data!"); + finish(EXIT_FAILURE); + } memcpy(output+offset, buffer, n); offset += n; } @@ -45,18 +51,30 @@ unsigned int get_all_buf(int sock, char* output, unsigned int maxsize) if(n < 0 && errno != EAGAIN && errno != EWOULDBLOCK) { fprintf(stderr, "error in get_all_buf!"); - exit(EXIT_FAILURE); + finish(EXIT_FAILURE); } return offset; } -int done = 0; - void iter(void *arg) { - /* perform read write operations ... */ static char out[1024*2]; static int pos = 0; - int n = get_all_buf(SocketFD, out+pos, 1024-pos); + fd_set fdr; + int res; + + // make sure that sockfd is ready to read + FD_ZERO(&fdr); + FD_SET(sockfd, &fdr); + res = select(64, &fdr, NULL, NULL, NULL); + if (res == -1) { + perror("select failed"); + finish(EXIT_FAILURE); + } else if (!FD_ISSET(sockfd, &fdr)) { + return; + } + + // perform read write operations ... + int n = get_all_buf(sockfd, out+pos, 1024-pos); if (n) printf("read! %d\n", n); pos += n; if (pos >= EXPECTED_BYTES) { @@ -66,75 +84,57 @@ void iter(void *arg) { sum += out[i]; } - shutdown(SocketFD, SHUT_RDWR); - - close(SocketFD); - - done = 1; + shutdown(sockfd, SHUT_RDWR); + close(sockfd); printf("sum: %d\n", sum); - - emscripten_cancel_main_loop(); - -#if EMSCRIPTEN - int result = sum; - REPORT_RESULT(); -#endif + finish(sum); } } -int main(void) -{ +int main() { + struct sockaddr_in addr; + int res; + printf("hello from main page\n"); - struct sockaddr_in stSockAddr; - int Res; #if !TEST_DGRAM - SocketFD = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + sockfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); #else - SocketFD = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); + sockfd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); #endif - - if (-1 == SocketFD) - { + if (sockfd == -1) { perror("cannot create socket"); - exit(EXIT_FAILURE); + finish(EXIT_FAILURE); } - - memset(&stSockAddr, 0, sizeof(stSockAddr)); - - stSockAddr.sin_family = AF_INET; - stSockAddr.sin_port = htons(SOCKK); - Res = inet_pton(AF_INET, "127.0.0.1", &stSockAddr.sin_addr); - - if (0 > Res) { - perror("error: first parameter is not a valid address family"); - close(SocketFD); - exit(EXIT_FAILURE); - } else if (0 == Res) { - perror("char string (second parameter does not contain valid ipaddress)"); - close(SocketFD); - exit(EXIT_FAILURE); + fcntl(sockfd, F_SETFL, O_NONBLOCK); + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(SOCKK); + if (inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr) != 1) { + perror("inet_pton failed"); + finish(EXIT_FAILURE); } - - if (-1 == connect(SocketFD, (struct sockaddr *)&stSockAddr, sizeof(stSockAddr))) { + + res = connect(sockfd, (struct sockaddr *)&addr, sizeof(addr)); + if (res == -1 && errno != EINPROGRESS) { perror("connect failed"); - close(SocketFD); - exit(EXIT_FAILURE); - + finish(EXIT_FAILURE); } #if EMSCRIPTEN +#if EMBED_SIDE emscripten_run_script("console.log('adding iframe');" "var iframe = document.createElement('iframe');" "iframe.src = 'side.html';" "document.body.appendChild(iframe);" "console.log('added.');"); +#endif emscripten_set_main_loop(iter, 0, 0); #else - while (!done) iter(NULL); + while (1) iter(NULL); #endif return EXIT_SUCCESS; } - diff --git a/tests/websockets_bi_bigdata.c b/tests/websockets_bi_bigdata.c index 2039f83c77ebd..e35096499d61d 100644 --- a/tests/websockets_bi_bigdata.c +++ b/tests/websockets_bi_bigdata.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #if EMSCRIPTEN #include @@ -16,10 +17,17 @@ #define EXPECTED_BYTES DATA_SIZE -int SocketFD; +int sockfd; -unsigned int get_all_buf(int sock, char* output, unsigned int maxsize) -{ +void finish(int result) { + close(sockfd); +#if EMSCRIPTEN + REPORT_RESULT(); +#endif + exit(result); +} + +unsigned int get_all_buf(int sock, char* output, unsigned int maxsize) { int bytes; if (ioctl(sock, FIONREAD, &bytes)) return 0; if (bytes == 0) return 0; @@ -29,41 +37,51 @@ unsigned int get_all_buf(int sock, char* output, unsigned int maxsize) unsigned int offset = 0; while((errno = 0, (n = recv(sock, buffer, sizeof(buffer), 0))>0) || errno == EINTR) { - if(n>0) - { - if (((unsigned int) n)+offset > maxsize) { fprintf(stderr, "too much data!"); exit(EXIT_FAILURE); } + if (n > 0) { + if (((unsigned int) n)+offset > maxsize) { + fprintf(stderr, "too much data!"); + finish(EXIT_FAILURE); + } memcpy(output+offset, buffer, n); offset += n; } } - if(n < 0 && errno != EAGAIN && errno != EWOULDBLOCK) { + if (n < 0 && errno != EAGAIN && errno != EWOULDBLOCK) { fprintf(stderr, "error in get_all_buf!"); - exit(EXIT_FAILURE); + finish(EXIT_FAILURE); } return offset; } -int done = 0; - void iter(void *arg) { - /* perform read write operations ... */ static char out[EXPECTED_BYTES]; static int pos = 0; + fd_set fdr; + int res; + + // make sure that sockfd has finished connecting and is ready to read + FD_ZERO(&fdr); + FD_SET(sockfd, &fdr); + res = select(64, &fdr, NULL, NULL, NULL); + if (res == -1) { + perror("select failed"); + finish(EXIT_FAILURE); + return; + } else if (!FD_ISSET(sockfd, &fdr)) { + return; + } + + // perform read write operations ... printf("so far %d, expecting up to %d\n", pos, EXPECTED_BYTES-pos); - int n = get_all_buf(SocketFD, out+pos, EXPECTED_BYTES-pos); - if (n) printf("read! %d\n", n); - pos += n; + res = get_all_buf(sockfd, out+pos, EXPECTED_BYTES-pos); + if (res) printf("read! %d\n", res); + pos += res; if (pos >= EXPECTED_BYTES) { - shutdown(SocketFD, SHUT_RDWR); - - close(SocketFD); - - done = 1; + shutdown(sockfd, SHUT_RDWR); - emscripten_cancel_main_loop(); + close(sockfd); -#if EMSCRIPTEN char *comp = generateData(); int result = strcmp(comp, out); if (result != 0) { @@ -71,52 +89,35 @@ void iter(void *arg) { printf("%d:%d\n", comp[i], out[i]); } } - REPORT_RESULT(); -#endif + finish(result); } } -int main(void) -{ - printf("hello from main page\n"); +int main() { + struct sockaddr_in addr; + int res; - struct sockaddr_in stSockAddr; - int Res; - SocketFD = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + printf("hello from main page\n"); - if (-1 == SocketFD) - { + sockfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + if (sockfd == -1) { perror("cannot create socket"); - exit(EXIT_FAILURE); + finish(EXIT_FAILURE); } - - memset(&stSockAddr, 0, sizeof(stSockAddr)); - - stSockAddr.sin_family = AF_INET; - stSockAddr.sin_port = htons( -#if EMSCRIPTEN - 3993 -#else - 3992 -#endif - ); - Res = inet_pton(AF_INET, "127.0.0.1", &stSockAddr.sin_addr); - - if (0 > Res) { - perror("error: first parameter is not a valid address family"); - close(SocketFD); - exit(EXIT_FAILURE); - } else if (0 == Res) { - perror("char string (second parameter does not contain valid ipaddress)"); - close(SocketFD); - exit(EXIT_FAILURE); + fcntl(sockfd, F_SETFL, O_NONBLOCK); + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(SOCKK); + if (inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr) != 1) { + perror("inet_pton failed"); + finish(EXIT_FAILURE); } - if (-1 == connect(SocketFD, (struct sockaddr *)&stSockAddr, sizeof(stSockAddr))) { + res = connect(sockfd, (struct sockaddr *)&addr, sizeof(addr)); + if (res == -1 && errno != EINPROGRESS) { perror("connect failed"); - close(SocketFD); - exit(EXIT_FAILURE); - + finish(EXIT_FAILURE); } #if EMSCRIPTEN @@ -129,7 +130,7 @@ int main(void) "console.log('added.');"); emscripten_set_main_loop(iter, 3, 0); #else - while (!done) iter(NULL); + while (1) iter(NULL); #endif return EXIT_SUCCESS; diff --git a/tests/websockets_bi_listener.c b/tests/websockets_bi_listener.c deleted file mode 100644 index 6c3b17b12ec14..0000000000000 --- a/tests/websockets_bi_listener.c +++ /dev/null @@ -1,151 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#if EMSCRIPTEN -#include -#endif - -#define EXPECTED_BYTES 28 - -int ListenFD, SocketFD; - -unsigned int get_all_buf(int sock, char* output, unsigned int maxsize) -{ - int bytes; - if (ioctl(sock, FIONREAD, &bytes)) return 0; - if (bytes == 0) return 0; - - char buffer[1024]; - int n; - unsigned int offset = 0; - while((errno = 0, (n = recv(sock, buffer, sizeof(buffer), 0))>0) || - errno == EINTR) { - if(n>0) - { - if (((unsigned int) n)+offset > maxsize) { fprintf(stderr, "too much data!"); exit(EXIT_FAILURE); } - memcpy(output+offset, buffer, n); - offset += n; - } - } - - if(n < 0 && errno != EAGAIN && errno != EWOULDBLOCK) { - fprintf(stderr, "error in get_all_buf!"); - exit(EXIT_FAILURE); - } - return offset; -} - -int done = 0; - -void iter(void *arg) { - /* perform read write operations ... */ - static char out[1024*2]; - static int pos = 0; - int n = get_all_buf(SocketFD, out+pos, 1024-pos); - if (n) printf("read! %d\n", n); - pos += n; - if (pos >= EXPECTED_BYTES) { - int i, sum = 0; - for (i=0; i < pos; i++) { - printf("%x\n", out[i]); - sum += out[i]; - } - - shutdown(SocketFD, SHUT_RDWR); - - close(SocketFD); - - done = 1; - - printf("sum: %d\n", sum); - -#if EMSCRIPTEN - int result = sum; - REPORT_RESULT(); - emscripten_cancel_main_loop(); -#endif - } -} - -int main(void) -{ - struct sockaddr_in stSockAddr; - int Res; - ListenFD = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); - - if (-1 == ListenFD) - { - perror("cannot create socket"); - exit(EXIT_FAILURE); - } - - memset(&stSockAddr, 0, sizeof(stSockAddr)); - - stSockAddr.sin_family = AF_INET; - stSockAddr.sin_port = htons( -#if EMSCRIPTEN - 6993 -#else - 6995 -#endif - ); - Res = inet_pton(AF_INET, "127.0.0.1", &stSockAddr.sin_addr); - - if (0 > Res) { - perror("error: first parameter is not a valid address family"); - close(ListenFD); - exit(EXIT_FAILURE); - } else if (0 == Res) { - perror("char string (second parameter does not contain valid ipaddress)"); - close(ListenFD); - exit(EXIT_FAILURE); - } - - printf("bind..\n"); - - if (-1 == bind(ListenFD, (struct sockaddr *)&stSockAddr, sizeof(stSockAddr))) { - perror("bind failed"); - close(ListenFD); - exit(EXIT_FAILURE); - } - - printf("listen..\n"); - - if (-1 == listen(ListenFD, 50)) { - perror("listen failed"); - close(ListenFD); - exit(EXIT_FAILURE); - } - - printf("accept..\n"); - - struct sockaddr_in stSockAddr2; - socklen_t temp; - - if (-1 == (SocketFD = accept(ListenFD, (struct sockaddr *)&stSockAddr2, &temp))) { - perror("accept failed"); - close(ListenFD); - exit(EXIT_FAILURE); - } - -#if EMSCRIPTEN - emscripten_run_script("console.log('adding iframe');" - "var iframe = document.createElement('iframe');" - "iframe.src = 'side.html';" - "document.body.appendChild(iframe);" - "console.log('added.');"); - emscripten_set_main_loop(iter, 0, 0); -#else - while (!done) iter(NULL); -#endif - - return EXIT_SUCCESS; -} - diff --git a/tests/websockets_bi_side.c b/tests/websockets_bi_side.c index 1d557ed80a5f5..b891063289b96 100644 --- a/tests/websockets_bi_side.c +++ b/tests/websockets_bi_side.c @@ -3,10 +3,12 @@ #include #include #include +#include #include #include #include #include +#include #include #if EMSCRIPTEN #include @@ -14,62 +16,77 @@ #define EXPECTED_BYTES 5 -int main(void) -{ - struct sockaddr_in stSockAddr; - int Res; -#if !TEST_DGRAM - int SocketFD = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); -#else - int SocketFD = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); -#endif +int sockfd = -1; - if (-1 == SocketFD) - { - perror("cannot create socket"); - exit(EXIT_FAILURE); - } - - memset(&stSockAddr, 0, sizeof(stSockAddr)); +void finish(int result) { + close(sockfd); + exit(result); +} - stSockAddr.sin_family = AF_INET; - stSockAddr.sin_port = htons(SOCKK); - Res = inet_pton(AF_INET, "127.0.0.1", &stSockAddr.sin_addr); +void loop() { + fd_set fdw; + int res; - if (0 > Res) { - perror("error: first parameter is not a valid address family"); - close(SocketFD); - exit(EXIT_FAILURE); - } else if (0 == Res) { - perror("char string (second parameter does not contain valid ipaddress)"); - close(SocketFD); - exit(EXIT_FAILURE); + // Make sure that sockfd has actually finished connecting + // and is ready to read. + FD_ZERO(&fdw); + FD_SET(sockfd, &fdw); + res = select(64, NULL, &fdw, NULL, NULL); + if (res == -1) { + perror("select failed"); + finish(EXIT_FAILURE); + } else if (!FD_ISSET(sockfd, &fdw)) { + return; } - printf("connect..\n"); + char data[] = "hello from the other siide\n"; - if (-1 == connect(SocketFD, (struct sockaddr *)&stSockAddr, sizeof(stSockAddr))) { - perror("connect failed"); - close(SocketFD); - exit(EXIT_FAILURE); + printf("send..\n"); +#if TEST_FILE_OPS + res = write(sockfd, data, sizeof(data)); +#else + res = send(sockfd, data, sizeof(data), 0); +#endif + if (res == -1) { + if (errno != EAGAIN) { + perror("send error"); + finish(EXIT_FAILURE); + } + return; } -#if TEST_FILE_OPS - printf("write..\n"); + finish(EXIT_SUCCESS); +} - char data[] = "hello from the other siide (fileops)\n"; - write(SocketFD, data, sizeof(data)); +int main() { + struct sockaddr_in addr; + int res; +#if !TEST_DGRAM + sockfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); #else - printf("send..\n"); - - char data[] = "hello from the other siide\n"; - send(SocketFD, data, sizeof(data), 0); + sockfd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); #endif + if (sockfd == -1) { + perror("cannot create socket"); + finish(EXIT_FAILURE); + } + fcntl(sockfd, F_SETFL, O_NONBLOCK); + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(SOCKK); + if (inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr) != 1) { + perror("inet_pton failed"); + finish(EXIT_FAILURE); + } - printf("stall..\n"); + res = connect(sockfd, (struct sockaddr *)&addr, sizeof(addr)); + if (res == -1 && errno != EINPROGRESS) { + perror("connect failed"); + finish(EXIT_FAILURE); + } - //int bytes; - //while (1) ioctl(SocketFD, FIONREAD, &bytes); + emscripten_set_main_loop(loop, 0, 0); return EXIT_SUCCESS; } diff --git a/tests/websockets_bi_side_bigdata.c b/tests/websockets_bi_side_bigdata.c index 9b67fe4cacbf7..d131e6e0f975a 100644 --- a/tests/websockets_bi_side_bigdata.c +++ b/tests/websockets_bi_side_bigdata.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #if EMSCRIPTEN #include @@ -16,54 +17,74 @@ #define EXPECTED_BYTES 5 -void stall(void *arg) { -} +int sockfd = -1; +char *data = NULL; -int main(void) -{ - struct sockaddr_in stSockAddr; - int Res; - int SocketFD = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); +void finish(int result) { + close(sockfd); + exit(result); +} - if (-1 == SocketFD) - { - perror("cannot create socket"); - exit(EXIT_FAILURE); +void loop() { + fd_set fdw; + int res; + + // make sure that sockfd has finished connecting and is ready to write + FD_ZERO(&fdw); + FD_SET(sockfd, &fdw); + res = select(64, NULL, &fdw, NULL, NULL); + if (res == -1) { + perror("select failed"); + finish(EXIT_FAILURE); + return; + } else if (!FD_ISSET(sockfd, &fdw)) { + return; } - memset(&stSockAddr, 0, sizeof(stSockAddr)); + printf("send..\n"); - stSockAddr.sin_family = AF_INET; - stSockAddr.sin_port = htons(SOCKK); - Res = inet_pton(AF_INET, "127.0.0.1", &stSockAddr.sin_addr); + res = send(sockfd, data, DATA_SIZE, 0); + if (res == -1) { + if (errno != EAGAIN) { + perror("send error"); + finish(EXIT_FAILURE); + } + return; + } - if (0 > Res) { - perror("error: first parameter is not a valid address family"); - close(SocketFD); - exit(EXIT_FAILURE); - } else if (0 == Res) { - perror("char string (second parameter does not contain valid ipaddress)"); - close(SocketFD); + finish(EXIT_SUCCESS); +} + +int main() { + struct sockaddr_in addr; + int res; + + sockfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + if (sockfd == -1) { + perror("cannot create socket"); exit(EXIT_FAILURE); } + fcntl(sockfd, F_SETFL, O_NONBLOCK); + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(SOCKK); + if (inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr) != 1) { + perror("inet_pton failed"); + finish(EXIT_FAILURE); + } printf("connect..\n"); - if (-1 == connect(SocketFD, (struct sockaddr *)&stSockAddr, sizeof(stSockAddr))) { + res = connect(sockfd, (struct sockaddr *)&addr, sizeof(addr)); + if (res == -1 && errno != EINPROGRESS) { perror("connect failed"); - close(SocketFD); - exit(EXIT_FAILURE); + finish(EXIT_FAILURE); } - printf("send..\n"); - - char *data = generateData(); - send(SocketFD, data, DATA_SIZE, 0); - - printf("stall..\n"); + data = generateData(); - emscripten_set_main_loop(stall, 1, 0); + emscripten_set_main_loop(loop, 1, 0); return EXIT_SUCCESS; -} - +} \ No newline at end of file diff --git a/tests/websockets_gethostbyname.c b/tests/websockets_gethostbyname.c index 1580d9a7654f8..59c55ba153820 100644 --- a/tests/websockets_gethostbyname.c +++ b/tests/websockets_gethostbyname.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #if EMSCRIPTEN #include @@ -14,10 +15,17 @@ #define EXPECTED_BYTES 5 -int SocketFD; +int sockfd; -unsigned int get_all_buf(int sock, char* output, unsigned int maxsize) -{ +void finish(int result) { + close(sockfd); +#if EMSCRIPTEN + REPORT_RESULT(); +#endif + exit(result); +} + +unsigned int get_all_buf(int sock, char* output, unsigned int maxsize) { int bytes; if (ioctl(sock, FIONREAD, &bytes)) return 0; if (bytes == 0) return 0; @@ -27,28 +35,42 @@ unsigned int get_all_buf(int sock, char* output, unsigned int maxsize) unsigned int offset = 0; while((errno = 0, (n = recv(sock, buffer, sizeof(buffer), 0))>0) || errno == EINTR) { - if(n>0) - { - if (((unsigned int) n)+offset > maxsize) { fprintf(stderr, "too much data!"); exit(EXIT_FAILURE); } + if (n > 0) { + if (((unsigned int) n)+offset > maxsize) { + fprintf(stderr, "too much data!"); + finish(EXIT_FAILURE); + } memcpy(output+offset, buffer, n); offset += n; } } - if(n < 0 && errno != EAGAIN && errno != EWOULDBLOCK) { - fprintf(stderr, "error in get_all_buf!"); - exit(EXIT_FAILURE); + if (n < 0 && errno != EAGAIN && errno != EWOULDBLOCK) { + perror("error in get_all_buf!"); + finish(EXIT_FAILURE); } return offset; } -int done = 0; - void iter() { - /* perform read write operations ... */ static char out[1024*2]; static int pos = 0; - int n = get_all_buf(SocketFD, out+pos, 1024-pos); + fd_set fdr; + int res; + + // make sure that sockfd has finished connecting and is ready to read + FD_ZERO(&fdr); + FD_SET(sockfd, &fdr); + res = select(64, &fdr, NULL, NULL, NULL); + if (res == -1) { + perror("select failed"); + finish(EXIT_FAILURE); + } else if (!FD_ISSET(sockfd, &fdr)) { + return; + } + + // perform read write operations ... + int n = get_all_buf(sockfd, out+pos, 1024-pos); if (n) printf("read! %d\n", n); pos += n; if (pos >= EXPECTED_BYTES) { @@ -58,73 +80,57 @@ void iter() { sum += out[i]; } - shutdown(SocketFD, SHUT_RDWR); + shutdown(sockfd, SHUT_RDWR); - close(SocketFD); - - done = 1; + close(sockfd); printf("sum: %d\n", sum); -#if EMSCRIPTEN - int result = sum; - REPORT_RESULT(); -#endif + finish(sum); } } -int main(void) -{ - struct sockaddr_in stSockAddr; - int Res; - SocketFD = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); +int main() { + struct sockaddr_in addr; + int res; - if (-1 == SocketFD) - { + sockfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + if (sockfd == -1) { perror("cannot create socket"); - exit(EXIT_FAILURE); + finish(EXIT_FAILURE); } + fcntl(sockfd, F_SETFL, O_NONBLOCK); - memset(&stSockAddr, 0, sizeof(stSockAddr)); - - stSockAddr.sin_family = AF_INET; - stSockAddr.sin_port = htons(7001); - + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(SOCKK); struct hostent *host0 = gethostbyname("test.com"); // increment hostname counter to check for possible but at 0,0 not differentiating low/high struct hostent *host = gethostbyname("localhost"); - char **addr_list = host->h_addr_list; - int *addr = (int*)*addr_list; - printf("raw addr: %d\n", *addr); + char **raw_addr_list = host->h_addr_list; + int *raw_addr = (int*)*raw_addr_list; + printf("raw addr: %d\n", *raw_addr); char name[INET_ADDRSTRLEN]; - if (!inet_ntop(AF_INET, addr, name, sizeof(name))) { + if (!inet_ntop(AF_INET, raw_addr, name, sizeof(name))) { printf("could not figure out name\n"); - return 0; + finish(EXIT_FAILURE); } printf("localhost has 'ip' of %s\n", name); - Res = inet_pton(AF_INET, name, &stSockAddr.sin_addr); - - if (0 > Res) { - perror("error: first parameter is not a valid address family"); - close(SocketFD); - exit(EXIT_FAILURE); - } else if (0 == Res) { - perror("char string (second parameter does not contain valid ipaddress)"); - close(SocketFD); - exit(EXIT_FAILURE); + if (inet_pton(AF_INET, name, &addr.sin_addr) != 1) { + perror("inet_pton failed"); + finish(EXIT_FAILURE); } - if (-1 == connect(SocketFD, (struct sockaddr *)&stSockAddr, sizeof(stSockAddr))) { + res = connect(sockfd, (struct sockaddr *)&addr, sizeof(addr)); + if (res == -1 && errno != EINPROGRESS) { perror("connect failed"); - close(SocketFD); - exit(EXIT_FAILURE); - + finish(EXIT_FAILURE); } #if EMSCRIPTEN emscripten_set_main_loop(iter, 0, 0); #else - while (!done) iter(); + while (1) iter(); #endif return EXIT_SUCCESS; diff --git a/tests/websockets_partial.c b/tests/websockets_partial.c index f71160b7d6ff9..5fe3472195a39 100644 --- a/tests/websockets_partial.c +++ b/tests/websockets_partial.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -13,107 +14,98 @@ #include #endif -int SocketFD; -int done = 0; +int sockfd = -1; int sum = 0; +void finish(int result) { + close(sockfd); +#if EMSCRIPTEN + REPORT_RESULT(); +#endif + exit(result); +} + void iter(void *arg) { char buffer[1024]; char packetLength; - int n; + fd_set fdr; int i; - - if (done) { + int res; + + // make sure that sockfd is ready to read + FD_ZERO(&fdr); + FD_SET(sockfd, &fdr); + res = select(64, &fdr, NULL, NULL, NULL); + if (res == -1) { + perror("select failed"); + finish(EXIT_FAILURE); + } else if (!FD_ISSET(sockfd, &fdr)) { return; } - n = recv(SocketFD, buffer, 1, 0); - - if (n == -1) { + res = recv(sockfd, buffer, 1, 0); + if (res == -1) { if (errno == EAGAIN || errno == EWOULDBLOCK) { return; //try again } - fprintf(stderr, "unexcepted end of data"); - exit(EXIT_FAILURE); + perror("unexcepted end of data"); + finish(EXIT_FAILURE); } - if (n != 1) { - fprintf(stderr, "should read 1 byte"); - exit(EXIT_FAILURE); + if (res != 1) { + perror("should read 1 byte"); + finish(EXIT_FAILURE); } packetLength = buffer[0]; - n = recv(SocketFD, buffer, packetLength, 0); + res = recv(sockfd, buffer, packetLength, 0); - printf("got %d,%d\n", n, packetLength); + printf("got %d,%d\n", res, packetLength); - if (n != packetLength) { - fprintf(stderr, "lost packet data, expected: %d readed: %d", packetLength, n); - exit(EXIT_FAILURE); + if (res != packetLength) { + fprintf(stderr, "lost packet data, expected: %d readed: %d", packetLength, res); + finish(EXIT_FAILURE); } for (i = 0; i < packetLength; ++i) { if (buffer[i] != i+1) { fprintf(stderr, "packet corrupted, expected: %d, actual: %d", i+1, buffer[i]); - exit(EXIT_FAILURE); + finish(EXIT_FAILURE); } sum += buffer[i]; } if (packetLength == buffer[0]) { // \x01\x01 - end marker - shutdown(SocketFD, SHUT_RDWR); - close(SocketFD); - done = 1; - - #if EMSCRIPTEN - printf("sum: %d\n", sum); - int result = sum; - REPORT_RESULT(); - #endif + printf("sum: %d\n", sum); + finish(sum); } } -int main(void) -{ - struct sockaddr_in stSockAddr; - int Res; - SocketFD = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); +int main() { + struct sockaddr_in addr; + int res; - if (-1 == SocketFD) - { + sockfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + if (sockfd == -1) { perror("cannot create socket"); exit(EXIT_FAILURE); } - - memset(&stSockAddr, 0, sizeof(stSockAddr)); - - stSockAddr.sin_family = AF_INET; - stSockAddr.sin_port = htons( -#if EMSCRIPTEN - 8991 -#else - 8990 -#endif - ); - Res = inet_pton(AF_INET, "127.0.0.1", &stSockAddr.sin_addr); - - if (0 > Res) { - perror("error: first parameter is not a valid address family"); - close(SocketFD); - exit(EXIT_FAILURE); - } else if (0 == Res) { - perror("char string (second parameter does not contain valid ipaddress)"); - close(SocketFD); - exit(EXIT_FAILURE); + fcntl(sockfd, F_SETFL, O_NONBLOCK); + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(SOCKK); + if (inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr) != 1) { + perror("inet_pton failed"); + finish(EXIT_FAILURE); } - if (-1 == connect(SocketFD, (struct sockaddr *)&stSockAddr, sizeof(stSockAddr))) { + res = connect(sockfd, (struct sockaddr *)&addr, sizeof(addr)); + if (res == -1 && errno != EINPROGRESS) { perror("connect failed"); - close(SocketFD); - exit(EXIT_FAILURE); - + finish(EXIT_FAILURE); } #if EMSCRIPTEN diff --git a/tests/websockets_select.c b/tests/websockets_select.c index b8ab90914968f..e05bd4c89f293 100644 --- a/tests/websockets_select.c +++ b/tests/websockets_select.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #if EMSCRIPTEN @@ -15,81 +16,83 @@ #define EXPECTED_BYTES 5 -int SocketFD; +int sockfd = -1; -int done = 0; +void finish(int result) { + close(sockfd); +#if EMSCRIPTEN + REPORT_RESULT(); +#endif + exit(result); +} + +void iter(void *arg) { + static int retries = 0; -void iter(void *arg) { fd_set sett; FD_ZERO(&sett); - FD_SET(SocketFD, &sett); + FD_SET(sockfd, &sett); - // The error should happen here - int select_says_yes = select(64, &sett, NULL, NULL, NULL); - if( select_says_yes == -1 ){ - printf( "Connection to websocket server failed as expected." ); - perror( "Error message" ); - int result = 266; - REPORT_RESULT(); - done = 1; + // currently, we've connected to a closed server port. + // the initial async connect "succeeded" and select + // should say that the socket is ready for a non-blocking + // read, however, the read should be 0 sized signalling + // that the remote end has closed. + int handles = select(64, &sett, NULL, NULL, NULL); + if (handles == -1) { + perror("select failed"); + finish(EXIT_FAILURE); } - assert(!select_says_yes); - done = 1; + if (FD_ISSET(sockfd, &sett)) { + char buffer[1024]; + int n = recv(sockfd, buffer, sizeof(buffer), 0); + if (n == -1 && retries++ > 10) { + perror("revv failed"); + finish(EXIT_FAILURE); + } else if (!n) { + perror("Connection to websocket server failed as expected."); + finish(266); + } + } } // This is for testing a websocket connection to a closed server port. // The connect call will succeed (due to the asynchronous websocket // behavior) but once the underlying websocket system realized that // the connection cannot be established, the next select call will fail. -int main(void) -{ - struct sockaddr_in stSockAddr; - int Res; - SocketFD = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); +int main() { + struct sockaddr_in addr; + int res; - if (-1 == SocketFD) - { + sockfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + if (sockfd == -1) { perror("cannot create socket"); - exit(EXIT_FAILURE); + finish(EXIT_FAILURE); } + fcntl(sockfd, F_SETFL, O_NONBLOCK); - memset(&stSockAddr, 0, sizeof(stSockAddr)); - - stSockAddr.sin_family = AF_INET; - stSockAddr.sin_port = htons( -#if EMSCRIPTEN - 8995 -#else - 8994 -#endif - ); - Res = inet_pton(AF_INET, "127.0.0.1", &stSockAddr.sin_addr); - - if (0 > Res) { - perror("error: first parameter is not a valid address family"); - close(SocketFD); - exit(EXIT_FAILURE); - } else if (0 == Res) { - perror("char string (second parameter does not contain valid ipaddress)"); - close(SocketFD); - exit(EXIT_FAILURE); + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(SOCKK); + if (inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr) != 1) { + perror("inet_pton failed"); + finish(EXIT_FAILURE); } // This call should succeed (even if the server port is closed) - if (-1 == connect(SocketFD, (struct sockaddr *)&stSockAddr, sizeof(stSockAddr))) { + res = connect(sockfd, (struct sockaddr *)&addr, sizeof(addr)); + if (res == -1 && errno != EINPROGRESS) { perror("connect failed"); - close(SocketFD); - exit(EXIT_FAILURE); - + finish(EXIT_FAILURE); } #if EMSCRIPTEN emscripten_set_main_loop(iter, 0, 0); #else - while (!done) iter(NULL); + while (1) iter(NULL); #endif - - return EXIT_SUCCESS; + + return EXIT_FAILURE; } diff --git a/tests/websockets_select_server_closes_connection.c b/tests/websockets_select_server_closes_connection.c index 6ce6d311c5e3c..4181b12bbc5c6 100644 --- a/tests/websockets_select_server_closes_connection.c +++ b/tests/websockets_select_server_closes_connection.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #if EMSCRIPTEN @@ -15,9 +16,15 @@ #define EXPECTED_BYTES 5 -int SocketFD; +int sockfd = -1; -int done = 0; +void finish(int result) { + close(sockfd); +#if EMSCRIPTEN + REPORT_RESULT(); +#endif + exit(result); +} void iter(void *arg) { static char readbuf[1024]; @@ -25,44 +32,39 @@ void iter(void *arg) { fd_set sett; FD_ZERO(&sett); - FD_SET(SocketFD, &sett); + FD_SET(sockfd, &sett); + + int res = select(64, &sett, NULL, NULL, NULL); - if( readPos < 7 ){ - // still reading - int selectRes = select(64, &sett, NULL, NULL, NULL); + if (res == -1) { + perror("select failed"); + finish(EXIT_FAILURE); + } else if (res == 0) { + return; + } else if (res > 0) { + assert(FD_ISSET(sockfd, &sett)); - if( selectRes == 0 ) + int bytesRead = recv(sockfd, readbuf+readPos, 7-readPos, 0); + if (bytesRead == -1) { + if (errno != EAGAIN) { + perror("recv error"); + finish(EXIT_FAILURE); + } + // try again return; - - if( selectRes == -1 ){ - perror( "Connection to websocket server failed" ); - exit(EXIT_FAILURE); } - if( selectRes > 0 ){ - assert(FD_ISSET(SocketFD, &sett)); - - int bytesRead = recv( SocketFD, readbuf+readPos, 7-readPos, 0 ); + + if (readPos < 7) { readPos += bytesRead; + } else { + if (!bytesRead) { + perror("Connection to websocket server was closed as expected"); + finish(266); + } else { + perror("Connection to websocket server was not closed"); + finish(EXIT_FAILURE); + } } - } else { - // here the server should have closed the connection - int selectRes = select(64, &sett, NULL, NULL, NULL); - - if( selectRes == 0 ) - return; - - if( selectRes == -1 ){ - perror( "Connection to websocket server failed as expected" ); - int result = 266; - REPORT_RESULT(); - emscripten_cancel_main_loop(); - done = 1; - } - - if( selectRes > 0 ){ - printf( "Error: socket should not show up on select call anymore.\n" ); - exit(EXIT_FAILURE); - } } return; @@ -71,56 +73,40 @@ void iter(void *arg) { // Scenario: the server sends data and closes the connection after 7 bytes. // This test should provoke the situation in which the underlying // tcp connection has been torn down already but there is still data -// in the inQueue. The select call has to succeed as long the queue +// in the queue. The select call has to succeed as long the queue // still contains data and only then start to throw errors. -int main(void) -{ - struct sockaddr_in stSockAddr; - int Res; - SocketFD = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); +int main() { + struct sockaddr_in addr; + int res; - if (-1 == SocketFD) - { + sockfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + if (sockfd == -1) { perror("cannot create socket"); - exit(EXIT_FAILURE); + finish(EXIT_FAILURE); } + fcntl(sockfd, F_SETFL, O_NONBLOCK); - memset(&stSockAddr, 0, sizeof(stSockAddr)); - - stSockAddr.sin_family = AF_INET; - stSockAddr.sin_port = htons( -#if EMSCRIPTEN - 8995 -#else - 8994 -#endif - ); - Res = inet_pton(AF_INET, "127.0.0.1", &stSockAddr.sin_addr); - - if (0 > Res) { - perror("error: first parameter is not a valid address family"); - close(SocketFD); - exit(EXIT_FAILURE); - } else if (0 == Res) { - perror("char string (second parameter does not contain valid ipaddress)"); - close(SocketFD); - exit(EXIT_FAILURE); + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(SOCKK); + if (inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr) != 1) { + perror("inet_pton failed"); + finish(EXIT_FAILURE); } // This call should succeed (even if the server port is closed) - if (-1 == connect(SocketFD, (struct sockaddr *)&stSockAddr, sizeof(stSockAddr))) { + res = connect(sockfd, (struct sockaddr *)&addr, sizeof(addr)); + if (res == -1 && errno != EINPROGRESS) { perror("connect failed"); - close(SocketFD); - exit(EXIT_FAILURE); - + finish(EXIT_FAILURE); } #if EMSCRIPTEN emscripten_set_main_loop(iter, 0, 0); #else - while (!done) iter(NULL); + while (1) iter(NULL); #endif - return EXIT_SUCCESS; + return EXIT_FAILURE; } diff --git a/tests/websockets_select_server_closes_connection_rw.c b/tests/websockets_select_server_closes_connection_rw.c index dd0913bfa1be2..f7e19aca31145 100644 --- a/tests/websockets_select_server_closes_connection_rw.c +++ b/tests/websockets_select_server_closes_connection_rw.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #if EMSCRIPTEN @@ -15,9 +16,15 @@ #define EXPECTED_BYTES 5 -int SocketFD; +int sockfd = -1; -int done = 0; +void finish(int result) { + close(sockfd); +#if EMSCRIPTEN + REPORT_RESULT(); +#endif + exit(result); +} void iter(void *arg) { static int state = 0; @@ -27,42 +34,40 @@ void iter(void *arg) { static int readPos = 0; int selectRes; ssize_t transferAmount; - fd_set sett; + fd_set sett; - - switch( state ){ + switch (state) { case 0: // writing 10 bytes to the server - // the socket in the read file descriptors has to result in a 0 return value - // because the connection exists, but there is no data yet - FD_ZERO( &sett ); - FD_SET(SocketFD, &sett); + // since the socket in the read file descriptors has no available data, + // select should tell us 0 handles are ready + FD_ZERO(&sett); + FD_SET(sockfd, &sett); selectRes = select(64, &sett, NULL, NULL, NULL); - if( selectRes != 0 ){ - printf( "case 0: read select != 0\n" ); - exit(EXIT_FAILURE); + if (selectRes != 0) { + printf("case 0: read select != 0 (%d)\n", selectRes); + finish(EXIT_FAILURE); } // the socket in the write file descriptors has to result in either a 0 or 1 // the connection either is setting up or is established and writing is possible - FD_ZERO( &sett ); - FD_SET(SocketFD, &sett); + FD_ZERO(&sett); + FD_SET(sockfd, &sett); selectRes = select(64, NULL, &sett, NULL, NULL); - if( selectRes == -1 ){ - printf( "case 0: write select == -1\n" ); - exit(EXIT_FAILURE); - } - if( selectRes == 0 ){ + if (selectRes == -1) { + printf("case 0: write select == -1\n"); + finish(EXIT_FAILURE); + } else if (selectRes == 0) { return; } // send a single byte - transferAmount = send( SocketFD, writebuf+writePos, 1, 0 ); + transferAmount = send(sockfd, writebuf+writePos, 1, 0); writePos += transferAmount; // after 10 bytes switch to next state - if( writePos >= 10 ){ + if (writePos >= 10) { state = 1; } break; @@ -70,79 +75,86 @@ void iter(void *arg) { case 1: // wait until we can read one byte to make sure the server // has sent the data and then closed the connection - FD_ZERO( &sett ); - FD_SET(SocketFD, &sett); + FD_ZERO(&sett); + FD_SET(sockfd, &sett); selectRes = select(64, &sett, NULL, NULL, NULL); - if( selectRes == -1 ){ - printf( "case 1: read selectRes == -1\n" ); - exit(EXIT_FAILURE); - } - if( selectRes == 0 ) + if (selectRes == -1) { + printf("case 1: read selectRes == -1\n"); + finish(EXIT_FAILURE); + } else if (selectRes == 0) { return; + } // read a single byte - transferAmount = recv( SocketFD, readbuf+readPos, 1, 0 ); + transferAmount = recv(sockfd, readbuf+readPos, 1, 0); readPos += transferAmount; // if successfully reading 1 byte, switch to next state - if( readPos >= 1 ){ + if (readPos >= 1) { state = 2; } break; case 2: - // calling select with the socket in the write file descriptors has - // to fail because the tcp network connection is already down - FD_ZERO( &sett ); - FD_SET(SocketFD, &sett); + // calling select with the socket in the write file descriptors should + // succeed, but the socket should not set in the set. + FD_ZERO(&sett); + FD_SET(sockfd, &sett); selectRes = select(64, NULL, &sett, NULL, NULL); - if( selectRes != -1 ){ - printf( "case 2: write selectRes != -1\n" ); - exit(EXIT_FAILURE); + if (selectRes != 0 || FD_ISSET(sockfd, &sett)) { + printf("case 2: write selectRes != 0 || FD_ISSET(sockfd, &sett)\n"); + finish(EXIT_FAILURE); } // calling select with the socket in the read file descriptors // has to succeed because there is still data in the inQueue - FD_ZERO( &sett ); - FD_SET(SocketFD, &sett); + FD_ZERO(&sett); + FD_SET(sockfd, &sett); selectRes = select(64, &sett, NULL, NULL, NULL); - if( selectRes != 1 ){ - printf( "case 2: read selectRes != 1\n" ); - exit(EXIT_FAILURE); - } - if( selectRes == 0 ) + if (selectRes != 1) { + printf("case 2: read selectRes != 1\n"); + finish(EXIT_FAILURE); + } else if (selectRes == 0) { return; + } // read a single byte - transferAmount = recv( SocketFD, readbuf+readPos, 1, 0 ); + transferAmount = recv(sockfd, readbuf+readPos, 1, 0); readPos += transferAmount; // with 10 bytes read the inQueue is empty => switch state - if( readPos >= 10 ){ + if (readPos >= 10) { state = 3; } break; case 3: // calling select with the socket in the read file descriptors - // now also has to fail as the inQueue is empty - FD_ZERO( &sett ); - FD_SET(SocketFD, &sett); + // should succeed + FD_ZERO(&sett); + FD_SET(sockfd, &sett); selectRes = select(64, &sett, NULL, NULL, NULL); - if( selectRes != -1 ){ - printf( "case 3: read selectRes != -1\n" ); - exit(EXIT_FAILURE); + if (selectRes != 1) { + printf("case 3: read selectRes != 1\n"); + finish(EXIT_FAILURE); + } + + // but recv should return 0 signaling the remote + // end has closed the connection. + transferAmount = recv(sockfd, readbuf, 1, 0); + if (transferAmount) { + printf("case 3: read != 0\n"); + finish(EXIT_FAILURE); } // report back success, the 266 is just an arbitrary value without // deeper meaning - int result = 266; - REPORT_RESULT(); + finish(266); break; default: - printf( "Impossible state!\n" ); - exit(EXIT_FAILURE); + printf("Impossible state!\n"); + finish(EXIT_FAILURE); break; } @@ -160,52 +172,36 @@ void iter(void *arg) { // as there are still 10 bytes to read from the inQueue. So, for the same socket the // select call behaves differently depending on whether the socket is listed in the // read or write file descriptors. -int main(void) -{ - struct sockaddr_in stSockAddr; - int Res; - SocketFD = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); +int main() { + struct sockaddr_in addr; + int res; - if (-1 == SocketFD) - { + sockfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + if (sockfd == -1) { perror("cannot create socket"); - exit(EXIT_FAILURE); + finish(EXIT_FAILURE); } - - memset(&stSockAddr, 0, sizeof(stSockAddr)); - - stSockAddr.sin_family = AF_INET; - stSockAddr.sin_port = htons( -#if EMSCRIPTEN - 8999 -#else - 8998 -#endif - ); - Res = inet_pton(AF_INET, "127.0.0.1", &stSockAddr.sin_addr); - - if (0 > Res) { - perror("error: first parameter is not a valid address family"); - close(SocketFD); - exit(EXIT_FAILURE); - } else if (0 == Res) { - perror("char string (second parameter does not contain valid ipaddress)"); - close(SocketFD); - exit(EXIT_FAILURE); + fcntl(sockfd, F_SETFL, O_NONBLOCK); + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(SOCKK); + if (inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr) != 1) { + perror("inet_pton failed"); + finish(EXIT_FAILURE); } // This call should succeed (even if the server port is closed) - if (-1 == connect(SocketFD, (struct sockaddr *)&stSockAddr, sizeof(stSockAddr))) { + res = connect(sockfd, (struct sockaddr *)&addr, sizeof(addr)); + if (res == -1 && errno != EINPROGRESS) { perror("connect failed"); - close(SocketFD); - exit(EXIT_FAILURE); - + finish(EXIT_FAILURE); } #if EMSCRIPTEN emscripten_set_main_loop(iter, 0, 0); #else - while (!done) iter(NULL); + while (1) iter(NULL); #endif return EXIT_SUCCESS; From 337817bf6c153f7cc2441c67dbc67ba510ed6b45 Mon Sep 17 00:00:00 2001 From: Anthony Pesch Date: Wed, 7 Aug 2013 14:07:43 -0700 Subject: [PATCH 002/112] renamed socket tests --- tests/runner.py | 20 +++++++++---------- .../{websockets.c => sockets/test_sockets.c} | 0 .../test_sockets_bi.c} | 0 .../test_sockets_bi_bigdata.c} | 2 +- .../test_sockets_bi_side.c} | 0 .../test_sockets_bi_side_bigdata.c} | 2 +- .../test_sockets_bigdata.h} | 0 .../test_sockets_gethostbyname.c} | 0 .../test_sockets_partial.c} | 0 .../test_sockets_select.c} | 0 ...sockets_select_server_closes_connection.c} | 0 ...kets_select_server_closes_connection_rw.c} | 0 12 files changed, 12 insertions(+), 12 deletions(-) rename tests/{websockets.c => sockets/test_sockets.c} (100%) rename tests/{websockets_bi.c => sockets/test_sockets_bi.c} (100%) rename tests/{websockets_bi_bigdata.c => sockets/test_sockets_bi_bigdata.c} (98%) rename tests/{websockets_bi_side.c => sockets/test_sockets_bi_side.c} (100%) rename tests/{websockets_bi_side_bigdata.c => sockets/test_sockets_bi_side_bigdata.c} (98%) rename tests/{websockets_bigdata.h => sockets/test_sockets_bigdata.h} (100%) rename tests/{websockets_gethostbyname.c => sockets/test_sockets_gethostbyname.c} (100%) rename tests/{websockets_partial.c => sockets/test_sockets_partial.c} (100%) rename tests/{websockets_select.c => sockets/test_sockets_select.c} (100%) rename tests/{websockets_select_server_closes_connection.c => sockets/test_sockets_select_server_closes_connection.c} (100%) rename tests/{websockets_select_server_closes_connection_rw.c => sockets/test_sockets_select_server_closes_connection_rw.c} (100%) diff --git a/tests/runner.py b/tests/runner.py index 2bd26eb72ff93..dbf7cb4fa6562 100755 --- a/tests/runner.py +++ b/tests/runner.py @@ -13951,7 +13951,7 @@ def __exit__(self, *args, **kwargs): def test_sockets(self): try: with self.WebsockHarness(8990): - self.btest('websockets.c', expected='571', args=['-DSOCKK=8991']) + self.btest('sockets/test_sockets.c', expected='571', args=['-DSOCKK=8991']) finally: self.clean_pids() @@ -13978,7 +13978,7 @@ def partial(q): try: with self.WebsockHarness(8990, partial): - self.btest('websockets_partial.c', expected='165', args=['-DSOCKK=8991']) + self.btest('sockets/test_sockets_partial.c', expected='165', args=['-DSOCKK=8991']) finally: self.clean_pids() @@ -13997,15 +13997,15 @@ def test_sockets_bi(self): print >> sys.stderr, 'test_websocket_bi datagram %d, fileops %d' % (datagram, fileops) with self.WebsockHarness(6992, self.make_relay_server(6992, 6994)): with self.WebsockHarness(6994, no_server=True): - Popen([PYTHON, EMCC, path_from_root('tests', 'websockets_bi_side.c'), '-o', 'side.html', '-DSOCKK=6995', '-DTEST_DGRAM=%d' % datagram]).communicate() - self.btest('websockets_bi.c', expected='2499', args=['-DSOCKK=6993', '-DTEST_DGRAM=%d' % datagram, '-DTEST_FILE_OPS=%s' % fileops, '-DEMBED_SIDE']) + Popen([PYTHON, EMCC, path_from_root('tests', 'sockets/test_sockets_bi_side.c'), '-o', 'side.html', '-DSOCKK=6995', '-DTEST_DGRAM=%d' % datagram]).communicate() + self.btest('sockets/test_sockets_bi.c', expected='2499', args=['-DSOCKK=6993', '-DTEST_DGRAM=%d' % datagram, '-DTEST_FILE_OPS=%s' % fileops, '-DEMBED_SIDE']) finally: self.clean_pids() def test_sockets_gethostbyname(self): try: with self.WebsockHarness(7000): - self.btest('websockets_gethostbyname.c', expected='571', args=['-O2', '-DSOCKK=7001']) + self.btest('sockets/test_sockets_gethostbyname.c', expected='571', args=['-O2', '-DSOCKK=7001']) finally: self.clean_pids() @@ -14013,8 +14013,8 @@ def test_sockets_bi_bigdata(self): try: with self.WebsockHarness(3992, self.make_relay_server(3992, 3994)): with self.WebsockHarness(3994, no_server=True): - Popen([PYTHON, EMCC, path_from_root('tests', 'websockets_bi_side_bigdata.c'), '-o', 'side.html', '-DSOCKK=3995', '-s', 'SOCKET_DEBUG=0', '-I' + path_from_root('tests')]).communicate() - self.btest('websockets_bi_bigdata.c', expected='0', args=['-DSOCKK=3993', '-s', 'SOCKET_DEBUG=0', '-I' + path_from_root('tests')]) + Popen([PYTHON, EMCC, path_from_root('tests', 'sockets/test_sockets_bi_side_bigdata.c'), '-o', 'side.html', '-DSOCKK=3995', '-s', 'SOCKET_DEBUG=0', '-I' + path_from_root('tests/sockets')]).communicate() + self.btest('sockets/test_sockets_bi_bigdata.c', expected='0', args=['-DSOCKK=3993', '-s', 'SOCKET_DEBUG=0', '-I' + path_from_root('tests/sockets')]) finally: self.clean_pids() @@ -14027,7 +14027,7 @@ def closedServer(q): ssock.bind(("127.0.0.1", 8994)) try: with self.WebsockHarness(8994, closedServer): - self.btest('websockets_select.c', expected='266', args=['-DSOCKK=8995']) + self.btest('sockets/test_sockets_select.c', expected='266', args=['-DSOCKK=8995']) finally: self.clean_pids() @@ -14047,7 +14047,7 @@ def closingServer(q): try: with self.WebsockHarness(8994, closingServer): - self.btest('websockets_select_server_closes_connection.c', expected='266', args=['-DSOCKK=8995']) + self.btest('sockets/test_sockets_select_server_closes_connection.c', expected='266', args=['-DSOCKK=8995']) finally: self.clean_pids() @@ -14079,7 +14079,7 @@ def closingServer_rw(q): try: with self.WebsockHarness(8998, closingServer_rw): - self.btest('websockets_select_server_closes_connection_rw.c', expected='266', args=['-DSOCKK=8999']) + self.btest('sockets/test_sockets_select_server_closes_connection_rw.c', expected='266', args=['-DSOCKK=8999']) finally: self.clean_pids() diff --git a/tests/websockets.c b/tests/sockets/test_sockets.c similarity index 100% rename from tests/websockets.c rename to tests/sockets/test_sockets.c diff --git a/tests/websockets_bi.c b/tests/sockets/test_sockets_bi.c similarity index 100% rename from tests/websockets_bi.c rename to tests/sockets/test_sockets_bi.c diff --git a/tests/websockets_bi_bigdata.c b/tests/sockets/test_sockets_bi_bigdata.c similarity index 98% rename from tests/websockets_bi_bigdata.c rename to tests/sockets/test_sockets_bi_bigdata.c index e35096499d61d..c1d8100e6c926 100644 --- a/tests/websockets_bi_bigdata.c +++ b/tests/sockets/test_sockets_bi_bigdata.c @@ -13,7 +13,7 @@ #include #endif -#include "websockets_bigdata.h" +#include "test_sockets_bigdata.h" #define EXPECTED_BYTES DATA_SIZE diff --git a/tests/websockets_bi_side.c b/tests/sockets/test_sockets_bi_side.c similarity index 100% rename from tests/websockets_bi_side.c rename to tests/sockets/test_sockets_bi_side.c diff --git a/tests/websockets_bi_side_bigdata.c b/tests/sockets/test_sockets_bi_side_bigdata.c similarity index 98% rename from tests/websockets_bi_side_bigdata.c rename to tests/sockets/test_sockets_bi_side_bigdata.c index d131e6e0f975a..e31029b635c76 100644 --- a/tests/websockets_bi_side_bigdata.c +++ b/tests/sockets/test_sockets_bi_side_bigdata.c @@ -13,7 +13,7 @@ #include #endif -#include "websockets_bigdata.h" +#include "test_sockets_bigdata.h" #define EXPECTED_BYTES 5 diff --git a/tests/websockets_bigdata.h b/tests/sockets/test_sockets_bigdata.h similarity index 100% rename from tests/websockets_bigdata.h rename to tests/sockets/test_sockets_bigdata.h diff --git a/tests/websockets_gethostbyname.c b/tests/sockets/test_sockets_gethostbyname.c similarity index 100% rename from tests/websockets_gethostbyname.c rename to tests/sockets/test_sockets_gethostbyname.c diff --git a/tests/websockets_partial.c b/tests/sockets/test_sockets_partial.c similarity index 100% rename from tests/websockets_partial.c rename to tests/sockets/test_sockets_partial.c diff --git a/tests/websockets_select.c b/tests/sockets/test_sockets_select.c similarity index 100% rename from tests/websockets_select.c rename to tests/sockets/test_sockets_select.c diff --git a/tests/websockets_select_server_closes_connection.c b/tests/sockets/test_sockets_select_server_closes_connection.c similarity index 100% rename from tests/websockets_select_server_closes_connection.c rename to tests/sockets/test_sockets_select_server_closes_connection.c diff --git a/tests/websockets_select_server_closes_connection_rw.c b/tests/sockets/test_sockets_select_server_closes_connection_rw.c similarity index 100% rename from tests/websockets_select_server_closes_connection_rw.c rename to tests/sockets/test_sockets_select_server_closes_connection_rw.c From 816e9c5698c3b285ffc3fe3bfd68fe290dca5034 Mon Sep 17 00:00:00 2001 From: Anthony Pesch Date: Wed, 7 Aug 2013 14:13:53 -0700 Subject: [PATCH 003/112] removed unused EMBED_SIDE define --- tests/runner.py | 2 +- tests/sockets/test_sockets_bi.c | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/runner.py b/tests/runner.py index dbf7cb4fa6562..6734c7e6fdac7 100755 --- a/tests/runner.py +++ b/tests/runner.py @@ -13998,7 +13998,7 @@ def test_sockets_bi(self): with self.WebsockHarness(6992, self.make_relay_server(6992, 6994)): with self.WebsockHarness(6994, no_server=True): Popen([PYTHON, EMCC, path_from_root('tests', 'sockets/test_sockets_bi_side.c'), '-o', 'side.html', '-DSOCKK=6995', '-DTEST_DGRAM=%d' % datagram]).communicate() - self.btest('sockets/test_sockets_bi.c', expected='2499', args=['-DSOCKK=6993', '-DTEST_DGRAM=%d' % datagram, '-DTEST_FILE_OPS=%s' % fileops, '-DEMBED_SIDE']) + self.btest('sockets/test_sockets_bi.c', expected='2499', args=['-DSOCKK=6993', '-DTEST_DGRAM=%d' % datagram, '-DTEST_FILE_OPS=%s' % fileops]) finally: self.clean_pids() diff --git a/tests/sockets/test_sockets_bi.c b/tests/sockets/test_sockets_bi.c index e19f7fe88a99b..4266d20ce1e76 100644 --- a/tests/sockets/test_sockets_bi.c +++ b/tests/sockets/test_sockets_bi.c @@ -124,13 +124,11 @@ int main() { } #if EMSCRIPTEN -#if EMBED_SIDE emscripten_run_script("console.log('adding iframe');" "var iframe = document.createElement('iframe');" "iframe.src = 'side.html';" "document.body.appendChild(iframe);" "console.log('added.');"); -#endif emscripten_set_main_loop(iter, 0, 0); #else while (1) iter(NULL); From 46c82708d50e839945fa24094fe352241be6a22e Mon Sep 17 00:00:00 2001 From: James Gregory Date: Thu, 8 Aug 2013 12:46:17 -0700 Subject: [PATCH 004/112] Basic touch event support in GLUT for mobile browsers. --- src/library_browser.js | 17 +++++++++++++++-- src/library_glut.js | 26 ++++++++++++++++++++------ 2 files changed, 35 insertions(+), 8 deletions(-) diff --git a/src/library_browser.js b/src/library_browser.js index 0db2cc44ec209..2b69e5d970ea2 100644 --- a/src/library_browser.js +++ b/src/library_browser.js @@ -446,8 +446,21 @@ mergeInto(LibraryManager.library, { // Otherwise, calculate the movement based on the changes // in the coordinates. var rect = Module["canvas"].getBoundingClientRect(); - var x = event.pageX - (window.scrollX + rect.left); - var y = event.pageY - (window.scrollY + rect.top); + var x, y; + if (event.type == 'touchstart' || + event.type == 'touchend' || + event.type == 'touchmove') { + var t = event.touches.item(0); + if (t) { + x = t.pageX - (window.scrollX + rect.left); + y = t.pageY - (window.scrollY + rect.top); + } else { + return; + } + } else { + x = event.pageX - (window.scrollX + rect.left); + y = event.pageY - (window.scrollY + rect.top); + } // the canvas might be CSS-scaled compared to its backbuffer; // SDL-using content will want mouse coordinates in terms diff --git a/src/library_glut.js b/src/library_glut.js index 36d4778797527..24474df033842 100644 --- a/src/library_glut.js +++ b/src/library_glut.js @@ -267,11 +267,19 @@ var LibraryGLUT = { // Ignore arguments GLUT.initTime = Date.now(); + var isTouchDevice = 'ontouchstart' in document.documentElement; + window.addEventListener("keydown", GLUT.onKeydown, true); window.addEventListener("keyup", GLUT.onKeyup, true); - window.addEventListener("mousemove", GLUT.onMousemove, true); - window.addEventListener("mousedown", GLUT.onMouseButtonDown, true); - window.addEventListener("mouseup", GLUT.onMouseButtonUp, true); + if (isTouchDevice) { + window.addEventListener("touchmove", GLUT.onMousemove, true); + window.addEventListener("touchstart", GLUT.onMouseButtonDown, true); + window.addEventListener("touchend", GLUT.onMouseButtonUp, true); + } else { + window.addEventListener("mousemove", GLUT.onMousemove, true); + window.addEventListener("mousedown", GLUT.onMouseButtonDown, true); + window.addEventListener("mouseup", GLUT.onMouseButtonUp, true); + } Browser.resizeListeners.push(function(width, height) { if (GLUT.reshapeFunc) { @@ -282,9 +290,15 @@ var LibraryGLUT = { __ATEXIT__.push({ func: function() { window.removeEventListener("keydown", GLUT.onKeydown, true); window.removeEventListener("keyup", GLUT.onKeyup, true); - window.removeEventListener("mousemove", GLUT.onMousemove, true); - window.removeEventListener("mousedown", GLUT.onMouseButtonDown, true); - window.removeEventListener("mouseup", GLUT.onMouseButtonUp, true); + if (isTouchDevice) { + window.removeEventListener("touchmove", GLUT.onMousemove, true); + window.removeEventListener("touchstart", GLUT.onMouseButtonDown, true); + window.removeEventListener("touchend", GLUT.onMouseButtonUp, true); + } else { + window.removeEventListener("mousemove", GLUT.onMousemove, true); + window.removeEventListener("mousedown", GLUT.onMouseButtonDown, true); + window.removeEventListener("mouseup", GLUT.onMouseButtonUp, true); + } Module["canvas"].width = Module["canvas"].height = 1; } }); }, From f6bb865bb52a9b92a699939f3558732488f787ab Mon Sep 17 00:00:00 2001 From: Bruce Mitchener Date: Fri, 9 Aug 2013 13:01:10 +0700 Subject: [PATCH 005/112] Another fix for including signal.h This is needed to work with musl includes. --- tests/stdio/test_rename.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/stdio/test_rename.c b/tests/stdio/test_rename.c index 20ffb513891a7..f15c814082a15 100644 --- a/tests/stdio/test_rename.c +++ b/tests/stdio/test_rename.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -104,4 +105,4 @@ int main() { setup(); test(); return EXIT_SUCCESS; -} \ No newline at end of file +} From 2bde63e16c12015cd97416ae70640ac8406c5903 Mon Sep 17 00:00:00 2001 From: Bruce Mitchener Date: Fri, 9 Aug 2013 13:26:57 +0700 Subject: [PATCH 006/112] Fix some typos. --- src/library.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/library.js b/src/library.js index 69f413d20d6c7..ec1a40b9a399a 100644 --- a/src/library.js +++ b/src/library.js @@ -1022,14 +1022,14 @@ LibraryManager.library = { if (relative.charAt(0) !== '.') { throw new FS.ErrnoError(ERRNO_CODES.ENOTEMPTY); } - // see if the new path alreay exists + // see if the new path already exists var new_node; try { new_node = FS.lookupNode(new_dir, new_name); } catch (e) { // not fatal } - // early out if nothing needs to changews + // early out if nothing needs to change if (old_node === new_node) { return; } @@ -1383,7 +1383,7 @@ LibraryManager.library = { stream.stream_ops.allocate(stream, offset, length); }, mmap: function(stream, buffer, offset, length, position, prot, flags) { - // TODO if PROT is PROT_WRITE, make sure we have write acccess + // TODO if PROT is PROT_WRITE, make sure we have write access if ((stream.flags & {{{ cDefine('O_ACCMODE') }}}) === {{{ cDefine('O_WRONLY')}}}) { throw new FS.ErrnoError(ERRNO_CODES.EACCES); } @@ -1695,7 +1695,7 @@ LibraryManager.library = { } }, // NOTE: This is weird to support stdout and stderr - // overrides in addition to print and printErr orverrides. + // overrides in addition to print and printErr overrides. default_tty_ops: { get_char: function(tty) { if (!tty.input.length) { @@ -2901,7 +2901,7 @@ LibraryManager.library = { value = ENV['PATH'] || '/'; break; case {{{ cDefine('_CS_POSIX_V6_WIDTH_RESTRICTED_ENVS') }}}: - // Mimicing glibc. + // Mimicking glibc. value = 'POSIX_V6_ILP32_OFF32\nPOSIX_V6_ILP32_OFFBIG'; break; case {{{ cDefine('_CS_GNU_LIBC_VERSION') }}}: From 47ca27a945c378db1baa82ec39fad9bd6b7e58fe Mon Sep 17 00:00:00 2001 From: Bruce Mitchener Date: Fri, 9 Aug 2013 13:58:34 +0700 Subject: [PATCH 007/112] Use cDefine to get numerical values for errors. --- src/library.js | 506 ++++++++++++++++++++++++------------------------ src/settings.js | 129 +++++++++++- 2 files changed, 381 insertions(+), 254 deletions(-) diff --git a/src/library.js b/src/library.js index 69f413d20d6c7..e85008f3dffe8 100644 --- a/src/library.js +++ b/src/library.js @@ -8148,262 +8148,262 @@ LibraryManager.library = { // ========================================================================== $ERRNO_CODES: { - EPERM: 1, - ENOENT: 2, - ESRCH: 3, - EINTR: 4, - EIO: 5, - ENXIO: 6, - E2BIG: 7, - ENOEXEC: 8, - EBADF: 9, - ECHILD: 10, - EAGAIN: 11, - EWOULDBLOCK: 11, - ENOMEM: 12, - EACCES: 13, - EFAULT: 14, - ENOTBLK: 15, - EBUSY: 16, - EEXIST: 17, - EXDEV: 18, - ENODEV: 19, - ENOTDIR: 20, - EISDIR: 21, - EINVAL: 22, - ENFILE: 23, - EMFILE: 24, - ENOTTY: 25, - ETXTBSY: 26, - EFBIG: 27, - ENOSPC: 28, - ESPIPE: 29, - EROFS: 30, - EMLINK: 31, - EPIPE: 32, - EDOM: 33, - ERANGE: 34, - ENOMSG: 35, - EIDRM: 36, - ECHRNG: 37, - EL2NSYNC: 38, - EL3HLT: 39, - EL3RST: 40, - ELNRNG: 41, - EUNATCH: 42, - ENOCSI: 43, - EL2HLT: 44, - EDEADLK: 45, - ENOLCK: 46, - EBADE: 50, - EBADR: 51, - EXFULL: 52, - ENOANO: 53, - EBADRQC: 54, - EBADSLT: 55, - EDEADLOCK: 56, - EBFONT: 57, - ENOSTR: 60, - ENODATA: 61, - ETIME: 62, - ENOSR: 63, - ENONET: 64, - ENOPKG: 65, - EREMOTE: 66, - ENOLINK: 67, - EADV: 68, - ESRMNT: 69, - ECOMM: 70, - EPROTO: 71, - EMULTIHOP: 74, - ELBIN: 75, - EDOTDOT: 76, - EBADMSG: 77, - EFTYPE: 79, - ENOTUNIQ: 80, - EBADFD: 81, - EREMCHG: 82, - ELIBACC: 83, - ELIBBAD: 84, - ELIBSCN: 85, - ELIBMAX: 86, - ELIBEXEC: 87, - ENOSYS: 88, - ENMFILE: 89, - ENOTEMPTY: 90, - ENAMETOOLONG: 91, - ELOOP: 92, - EOPNOTSUPP: 95, - EPFNOSUPPORT: 96, - ECONNRESET: 104, - ENOBUFS: 105, - EAFNOSUPPORT: 106, - EPROTOTYPE: 107, - ENOTSOCK: 108, - ENOPROTOOPT: 109, - ESHUTDOWN: 110, - ECONNREFUSED: 111, - EADDRINUSE: 112, - ECONNABORTED: 113, - ENETUNREACH: 114, - ENETDOWN: 115, - ETIMEDOUT: 116, - EHOSTDOWN: 117, - EHOSTUNREACH: 118, - EINPROGRESS: 119, - EALREADY: 120, - EDESTADDRREQ: 121, - EMSGSIZE: 122, - EPROTONOSUPPORT: 123, - ESOCKTNOSUPPORT: 124, - EADDRNOTAVAIL: 125, - ENETRESET: 126, - EISCONN: 127, - ENOTCONN: 128, - ETOOMANYREFS: 129, - EPROCLIM: 130, - EUSERS: 131, - EDQUOT: 132, - ESTALE: 133, - ENOTSUP: 134, - ENOMEDIUM: 135, - ENOSHARE: 136, - ECASECLASH: 137, - EILSEQ: 138, - EOVERFLOW: 139, - ECANCELED: 140, - ENOTRECOVERABLE: 141, - EOWNERDEAD: 142, - ESTRPIPE: 143 + EPERM: {{{ cDefine('EPERM') }}}, + ENOENT: {{{ cDefine('ENOENT') }}}, + ESRCH: {{{ cDefine('ESRCH') }}}, + EINTR: {{{ cDefine('EINTR') }}}, + EIO: {{{ cDefine('EIO') }}}, + ENXIO: {{{ cDefine('ENXIO') }}}, + E2BIG: {{{ cDefine('E2BIG') }}}, + ENOEXEC: {{{ cDefine('ENOEXEC') }}}, + EBADF: {{{ cDefine('EBADF') }}}, + ECHILD: {{{ cDefine('ECHILD') }}}, + EAGAIN: {{{ cDefine('EAGAIN') }}}, + EWOULDBLOCK: {{{ cDefine('EWOULDBLOCK') }}}, + ENOMEM: {{{ cDefine('ENOMEM') }}}, + EACCES: {{{ cDefine('EACCES') }}}, + EFAULT: {{{ cDefine('EFAULT') }}}, + ENOTBLK: {{{ cDefine('ENOTBLK') }}}, + EBUSY: {{{ cDefine('EBUSY') }}}, + EEXIST: {{{ cDefine('EEXIST') }}}, + EXDEV: {{{ cDefine('EXDEV') }}}, + ENODEV: {{{ cDefine('ENODEV') }}}, + ENOTDIR: {{{ cDefine('ENOTDIR') }}}, + EISDIR: {{{ cDefine('EISDIR') }}}, + EINVAL: {{{ cDefine('EINVAL') }}}, + ENFILE: {{{ cDefine('ENFILE') }}}, + EMFILE: {{{ cDefine('EMFILE') }}}, + ENOTTY: {{{ cDefine('ENOTTY') }}}, + ETXTBSY: {{{ cDefine('ETXTBSY') }}}, + EFBIG: {{{ cDefine('EFBIG') }}}, + ENOSPC: {{{ cDefine('ENOSPC') }}}, + ESPIPE: {{{ cDefine('ESPIPE') }}}, + EROFS: {{{ cDefine('EROFS') }}}, + EMLINK: {{{ cDefine('EMLINK') }}}, + EPIPE: {{{ cDefine('EPIPE') }}}, + EDOM: {{{ cDefine('EDOM') }}}, + ERANGE: {{{ cDefine('ERANGE') }}}, + ENOMSG: {{{ cDefine('ENOMSG') }}}, + EIDRM: {{{ cDefine('EIDRM') }}}, + ECHRNG: {{{ cDefine('ECHRNG') }}}, + EL2NSYNC: {{{ cDefine('EL2NSYNC') }}}, + EL3HLT: {{{ cDefine('EL3HLT') }}}, + EL3RST: {{{ cDefine('EL3RST') }}}, + ELNRNG: {{{ cDefine('ELNRNG') }}}, + EUNATCH: {{{ cDefine('EUNATCH') }}}, + ENOCSI: {{{ cDefine('ENOCSI') }}}, + EL2HLT: {{{ cDefine('EL2HLT') }}}, + EDEADLK: {{{ cDefine('EDEADLK') }}}, + ENOLCK: {{{ cDefine('ENOLCK') }}}, + EBADE: {{{ cDefine('EBADE') }}}, + EBADR: {{{ cDefine('EBADR') }}}, + EXFULL: {{{ cDefine('EXFULL') }}}, + ENOANO: {{{ cDefine('ENOANO') }}}, + EBADRQC: {{{ cDefine('EBADRQC') }}}, + EBADSLT: {{{ cDefine('EBADSLT') }}}, + EDEADLOCK: {{{ cDefine('EDEADLOCK') }}}, + EBFONT: {{{ cDefine('EBFONT') }}}, + ENOSTR: {{{ cDefine('ENOSTR') }}}, + ENODATA: {{{ cDefine('ENODATA') }}}, + ETIME: {{{ cDefine('ETIME') }}}, + ENOSR: {{{ cDefine('ENOSR') }}}, + ENONET: {{{ cDefine('ENONET') }}}, + ENOPKG: {{{ cDefine('ENOPKG') }}}, + EREMOTE: {{{ cDefine('EREMOTE') }}}, + ENOLINK: {{{ cDefine('ENOLINK') }}}, + EADV: {{{ cDefine('EADV') }}}, + ESRMNT: {{{ cDefine('ESRMNT') }}}, + ECOMM: {{{ cDefine('ECOMM') }}}, + EPROTO: {{{ cDefine('EPROTO') }}}, + EMULTIHOP: {{{ cDefine('EMULTIHOP') }}}, + ELBIN: {{{ cDefine('ELBIN') }}}, + EDOTDOT: {{{ cDefine('EDOTDOT') }}}, + EBADMSG: {{{ cDefine('EBADMSG') }}}, + EFTYPE: {{{ cDefine('EFTYPE') }}}, + ENOTUNIQ: {{{ cDefine('ENOTUNIQ') }}}, + EBADFD: {{{ cDefine('EBADFD') }}}, + EREMCHG: {{{ cDefine('EREMCHG') }}}, + ELIBACC: {{{ cDefine('ELIBACC') }}}, + ELIBBAD: {{{ cDefine('ELIBBAD') }}}, + ELIBSCN: {{{ cDefine('ELIBSCN') }}}, + ELIBMAX: {{{ cDefine('ELIBMAX') }}}, + ELIBEXEC: {{{ cDefine('ELIBEXEC') }}}, + ENOSYS: {{{ cDefine('ENOSYS') }}}, + ENMFILE: {{{ cDefine('ENMFILE') }}}, + ENOTEMPTY: {{{ cDefine('ENOTEMPTY') }}}, + ENAMETOOLONG: {{{ cDefine('ENAMETOOLONG') }}}, + ELOOP: {{{ cDefine('ELOOP') }}}, + EOPNOTSUPP: {{{ cDefine('EOPNOTSUPP') }}}, + EPFNOSUPPORT: {{{ cDefine('EPFNOSUPPORT') }}}, + ECONNRESET: {{{ cDefine('ECONNRESET') }}}, + ENOBUFS: {{{ cDefine('ENOBUFS') }}}, + EAFNOSUPPORT: {{{ cDefine('EAFNOSUPPORT') }}}, + EPROTOTYPE: {{{ cDefine('EPROTOTYPE') }}}, + ENOTSOCK: {{{ cDefine('ENOTSOCK') }}}, + ENOPROTOOPT: {{{ cDefine('ENOPROTOOPT') }}}, + ESHUTDOWN: {{{ cDefine('ESHUTDOWN') }}}, + ECONNREFUSED: {{{ cDefine('ECONNREFUSED') }}}, + EADDRINUSE: {{{ cDefine('EADDRINUSE') }}}, + ECONNABORTED: {{{ cDefine('ECONNABORTED') }}}, + ENETUNREACH: {{{ cDefine('ENETUNREACH') }}}, + ENETDOWN: {{{ cDefine('ENETDOWN') }}}, + ETIMEDOUT: {{{ cDefine('ETIMEDOUT') }}}, + EHOSTDOWN: {{{ cDefine('EHOSTDOWN') }}}, + EHOSTUNREACH: {{{ cDefine('EHOSTUNREACH') }}}, + EINPROGRESS: {{{ cDefine('EINPROGRESS') }}}, + EALREADY: {{{ cDefine('EALREADY') }}}, + EDESTADDRREQ: {{{ cDefine('EDESTADDRREQ') }}}, + EMSGSIZE: {{{ cDefine('EMSGSIZE') }}}, + EPROTONOSUPPORT: {{{ cDefine('EPROTONOSUPPORT') }}}, + ESOCKTNOSUPPORT: {{{ cDefine('ESOCKTNOSUPPORT') }}}, + EADDRNOTAVAIL: {{{ cDefine('EADDRNOTAVAIL') }}}, + ENETRESET: {{{ cDefine('ENETRESET') }}}, + EISCONN: {{{ cDefine('EISCONN') }}}, + ENOTCONN: {{{ cDefine('ENOTCONN') }}}, + ETOOMANYREFS: {{{ cDefine('ETOOMANYREFS') }}}, + EPROCLIM: {{{ cDefine('EPROCLIM') }}}, + EUSERS: {{{ cDefine('EUSERS') }}}, + EDQUOT: {{{ cDefine('EDQUOT') }}}, + ESTALE: {{{ cDefine('ESTALE') }}}, + ENOTSUP: {{{ cDefine('ENOTSUP') }}}, + ENOMEDIUM: {{{ cDefine('ENOMEDIUM') }}}, + ENOSHARE: {{{ cDefine('ENOSHARE') }}}, + ECASECLASH: {{{ cDefine('ECASECLASH') }}}, + EILSEQ: {{{ cDefine('EILSEQ') }}}, + EOVERFLOW: {{{ cDefine('EOVERFLOW') }}}, + ECANCELED: {{{ cDefine('ECANCELED') }}}, + ENOTRECOVERABLE: {{{ cDefine('ENOTRECOVERABLE') }}}, + EOWNERDEAD: {{{ cDefine('EOWNERDEAD') }}}, + ESTRPIPE: {{{ cDefine('ESTRPIPE') }}}, }, $ERRNO_MESSAGES: { 0: 'Success', - 1: 'Not super-user', - 2: 'No such file or directory', - 3: 'No such process', - 4: 'Interrupted system call', - 5: 'I/O error', - 6: 'No such device or address', - 7: 'Arg list too long', - 8: 'Exec format error', - 9: 'Bad file number', - 10: 'No children', - 11: 'No more processes', - 12: 'Not enough core', - 13: 'Permission denied', - 14: 'Bad address', - 15: 'Block device required', - 16: 'Mount device busy', - 17: 'File exists', - 18: 'Cross-device link', - 19: 'No such device', - 20: 'Not a directory', - 21: 'Is a directory', - 22: 'Invalid argument', - 23: 'Too many open files in system', - 24: 'Too many open files', - 25: 'Not a typewriter', - 26: 'Text file busy', - 27: 'File too large', - 28: 'No space left on device', - 29: 'Illegal seek', - 30: 'Read only file system', - 31: 'Too many links', - 32: 'Broken pipe', - 33: 'Math arg out of domain of func', - 34: 'Math result not representable', - 35: 'No message of desired type', - 36: 'Identifier removed', - 37: 'Channel number out of range', - 38: 'Level 2 not synchronized', - 39: 'Level 3 halted', - 40: 'Level 3 reset', - 41: 'Link number out of range', - 42: 'Protocol driver not attached', - 43: 'No CSI structure available', - 44: 'Level 2 halted', - 45: 'Deadlock condition', - 46: 'No record locks available', - 50: 'Invalid exchange', - 51: 'Invalid request descriptor', - 52: 'Exchange full', - 53: 'No anode', - 54: 'Invalid request code', - 55: 'Invalid slot', - 56: 'File locking deadlock error', - 57: 'Bad font file fmt', - 60: 'Device not a stream', - 61: 'No data (for no delay io)', - 62: 'Timer expired', - 63: 'Out of streams resources', - 64: 'Machine is not on the network', - 65: 'Package not installed', - 66: 'The object is remote', - 67: 'The link has been severed', - 68: 'Advertise error', - 69: 'Srmount error', - 70: 'Communication error on send', - 71: 'Protocol error', - 74: 'Multihop attempted', - 75: 'Inode is remote (not really error)', - 76: 'Cross mount point (not really error)', - 77: 'Trying to read unreadable message', - 79: 'Inappropriate file type or format', - 80: 'Given log. name not unique', - 81: 'f.d. invalid for this operation', - 82: 'Remote address changed', - 83: 'Can\t access a needed shared lib', - 84: 'Accessing a corrupted shared lib', - 85: '.lib section in a.out corrupted', - 86: 'Attempting to link in too many libs', - 87: 'Attempting to exec a shared library', - 88: 'Function not implemented', - 89: 'No more files', - 90: 'Directory not empty', - 91: 'File or path name too long', - 92: 'Too many symbolic links', - 95: 'Operation not supported on transport endpoint', - 96: 'Protocol family not supported', - 104: 'Connection reset by peer', - 105: 'No buffer space available', - 106: 'Address family not supported by protocol family', - 107: 'Protocol wrong type for socket', - 108: 'Socket operation on non-socket', - 109: 'Protocol not available', - 110: 'Can\'t send after socket shutdown', - 111: 'Connection refused', - 112: 'Address already in use', - 113: 'Connection aborted', - 114: 'Network is unreachable', - 115: 'Network interface is not configured', - 116: 'Connection timed out', - 117: 'Host is down', - 118: 'Host is unreachable', - 119: 'Connection already in progress', - 120: 'Socket already connected', - 121: 'Destination address required', - 122: 'Message too long', - 123: 'Unknown protocol', - 124: 'Socket type not supported', - 125: 'Address not available', - 126: 'ENETRESET', - 127: 'Socket is already connected', - 128: 'Socket is not connected', - 129: 'TOOMANYREFS', - 130: 'EPROCLIM', - 131: 'EUSERS', - 132: 'EDQUOT', - 133: 'ESTALE', - 134: 'Not supported', - 135: 'No medium (in tape drive)', - 136: 'No such host or network path', - 137: 'Filename exists with different case', - 138: 'EILSEQ', - 139: 'Value too large for defined data type', - 140: 'Operation canceled', - 141: 'State not recoverable', - 142: 'Previous owner died', - 143: 'Streams pipe error', + {{{ cDefine('EPERM') }}}: 'Not super-user', + {{{ cDefine('ENOENT') }}}: 'No such file or directory', + {{{ cDefine('ESRCH') }}}: 'No such process', + {{{ cDefine('EINTR') }}}: 'Interrupted system call', + {{{ cDefine('EIO') }}}: 'I/O error', + {{{ cDefine('ENXIO') }}}: 'No such device or address', + {{{ cDefine('E2BIG') }}}: 'Arg list too long', + {{{ cDefine('ENOEXEC') }}}: 'Exec format error', + {{{ cDefine('EBADF') }}}: 'Bad file number', + {{{ cDefine('ECHILD') }}}: 'No children', + {{{ cDefine('EWOULDBLOCK') }}}: 'No more processes', + {{{ cDefine('ENOMEM') }}}: 'Not enough core', + {{{ cDefine('EACCES') }}}: 'Permission denied', + {{{ cDefine('EFAULT') }}}: 'Bad address', + {{{ cDefine('ENOTBLK') }}}: 'Block device required', + {{{ cDefine('EBUSY') }}}: 'Mount device busy', + {{{ cDefine('EEXIST') }}}: 'File exists', + {{{ cDefine('EXDEV') }}}: 'Cross-device link', + {{{ cDefine('ENODEV') }}}: 'No such device', + {{{ cDefine('ENOTDIR') }}}: 'Not a directory', + {{{ cDefine('EISDIR') }}}: 'Is a directory', + {{{ cDefine('EINVAL') }}}: 'Invalid argument', + {{{ cDefine('ENFILE') }}}: 'Too many open files in system', + {{{ cDefine('EMFILE') }}}: 'Too many open files', + {{{ cDefine('ENOTTY') }}}: 'Not a typewriter', + {{{ cDefine('ETXTBSY') }}}: 'Text file busy', + {{{ cDefine('EFBIG') }}}: 'File too large', + {{{ cDefine('ENOSPC') }}}: 'No space left on device', + {{{ cDefine('ESPIPE') }}}: 'Illegal seek', + {{{ cDefine('EROFS') }}}: 'Read only file system', + {{{ cDefine('EMLINK') }}}: 'Too many links', + {{{ cDefine('EPIPE') }}}: 'Broken pipe', + {{{ cDefine('EDOM') }}}: 'Math arg out of domain of func', + {{{ cDefine('ERANGE') }}}: 'Math result not representable', + {{{ cDefine('ENOMSG') }}}: 'No message of desired type', + {{{ cDefine('EIDRM') }}}: 'Identifier removed', + {{{ cDefine('ECHRNG') }}}: 'Channel number out of range', + {{{ cDefine('EL2NSYNC') }}}: 'Level 2 not synchronized', + {{{ cDefine('EL3HLT') }}}: 'Level 3 halted', + {{{ cDefine('EL3RST') }}}: 'Level 3 reset', + {{{ cDefine('ELNRNG') }}}: 'Link number out of range', + {{{ cDefine('EUNATCH') }}}: 'Protocol driver not attached', + {{{ cDefine('ENOCSI') }}}: 'No CSI structure available', + {{{ cDefine('EL2HLT') }}}: 'Level 2 halted', + {{{ cDefine('EDEADLK') }}}: 'Deadlock condition', + {{{ cDefine('ENOLCK') }}}: 'No record locks available', + {{{ cDefine('EBADE') }}}: 'Invalid exchange', + {{{ cDefine('EBADR') }}}: 'Invalid request descriptor', + {{{ cDefine('EXFULL') }}}: 'Exchange full', + {{{ cDefine('ENOANO') }}}: 'No anode', + {{{ cDefine('EBADRQC') }}}: 'Invalid request code', + {{{ cDefine('EBADSLT') }}}: 'Invalid slot', + {{{ cDefine('EDEADLOCK') }}}: 'File locking deadlock error', + {{{ cDefine('EBFONT') }}}: 'Bad font file fmt', + {{{ cDefine('ENOSTR') }}}: 'Device not a stream', + {{{ cDefine('ENODATA') }}}: 'No data (for no delay io)', + {{{ cDefine('ETIME') }}}: 'Timer expired', + {{{ cDefine('ENOSR') }}}: 'Out of streams resources', + {{{ cDefine('ENONET') }}}: 'Machine is not on the network', + {{{ cDefine('ENOPKG') }}}: 'Package not installed', + {{{ cDefine('EREMOTE') }}}: 'The object is remote', + {{{ cDefine('ENOLINK') }}}: 'The link has been severed', + {{{ cDefine('EADV') }}}: 'Advertise error', + {{{ cDefine('ESRMNT') }}}: 'Srmount error', + {{{ cDefine('ECOMM') }}}: 'Communication error on send', + {{{ cDefine('EPROTO') }}}: 'Protocol error', + {{{ cDefine('EMULTIHOP') }}}: 'Multihop attempted', + {{{ cDefine('ELBIN') }}}: 'Inode is remote (not really error)', + {{{ cDefine('EDOTDOT') }}}: 'Cross mount point (not really error)', + {{{ cDefine('EBADMSG') }}}: 'Trying to read unreadable message', + {{{ cDefine('EFTYPE') }}}: 'Inappropriate file type or format', + {{{ cDefine('ENOTUNIQ') }}}: 'Given log. name not unique', + {{{ cDefine('EBADFD') }}}: 'f.d. invalid for this operation', + {{{ cDefine('EREMCHG') }}}: 'Remote address changed', + {{{ cDefine('ELIBACC') }}}: 'Can access a needed shared lib', + {{{ cDefine('ELIBBAD') }}}: 'Accessing a corrupted shared lib', + {{{ cDefine('ELIBSCN') }}}: '.lib section in a.out corrupted', + {{{ cDefine('ELIBMAX') }}}: 'Attempting to link in too many libs', + {{{ cDefine('ELIBEXEC') }}}: 'Attempting to exec a shared library', + {{{ cDefine('ENOSYS') }}}: 'Function not implemented', + {{{ cDefine('ENMFILE') }}}: 'No more files', + {{{ cDefine('ENOTEMPTY') }}}: 'Directory not empty', + {{{ cDefine('ENAMETOOLONG') }}}: 'File or path name too long', + {{{ cDefine('ELOOP') }}}: 'Too many symbolic links', + {{{ cDefine('EOPNOTSUPP') }}}: 'Operation not supported on transport endpoint', + {{{ cDefine('EPFNOSUPPORT') }}}: 'Protocol family not supported', + {{{ cDefine('ECONNRESET') }}}: 'Connection reset by peer', + {{{ cDefine('ENOBUFS') }}}: 'No buffer space available', + {{{ cDefine('EAFNOSUPPORT') }}}: 'Address family not supported by protocol family', + {{{ cDefine('EPROTOTYPE') }}}: 'Protocol wrong type for socket', + {{{ cDefine('ENOTSOCK') }}}: 'Socket operation on non-socket', + {{{ cDefine('ENOPROTOOPT') }}}: 'Protocol not available', + {{{ cDefine('ESHUTDOWN') }}}: 'Can\'t send after socket shutdown', + {{{ cDefine('ECONNREFUSED') }}}: 'Connection refused', + {{{ cDefine('EADDRINUSE') }}}: 'Address already in use', + {{{ cDefine('ECONNABORTED') }}}: 'Connection aborted', + {{{ cDefine('ENETUNREACH') }}}: 'Network is unreachable', + {{{ cDefine('ENETDOWN') }}}: 'Network interface is not configured', + {{{ cDefine('ETIMEDOUT') }}}: 'Connection timed out', + {{{ cDefine('EHOSTDOWN') }}}: 'Host is down', + {{{ cDefine('EHOSTUNREACH') }}}: 'Host is unreachable', + {{{ cDefine('EINPROGRESS') }}}: 'Connection already in progress', + {{{ cDefine('EALREADY') }}}: 'Socket already connected', + {{{ cDefine('EDESTADDRREQ') }}}: 'Destination address required', + {{{ cDefine('EMSGSIZE') }}}: 'Message too long', + {{{ cDefine('EPROTONOSUPPORT') }}}: 'Unknown protocol', + {{{ cDefine('ESOCKTNOSUPPORT') }}}: 'Socket type not supported', + {{{ cDefine('EADDRNOTAVAIL') }}}: 'Address not available', + {{{ cDefine('ENETRESET') }}}: 'ENETRESET', + {{{ cDefine('EISCONN') }}}: 'Socket is already connected', + {{{ cDefine('ENOTCONN') }}}: 'Socket is not connected', + {{{ cDefine('ETOOMANYREFS') }}}: 'TOOMANYREFS', + {{{ cDefine('EPROCLIM') }}}: 'EPROCLIM', + {{{ cDefine('EUSERS') }}}: 'EUSERS', + {{{ cDefine('EDQUOT') }}}: 'EDQUOT', + {{{ cDefine('ESTALE') }}}: 'ESTALE', + {{{ cDefine('ENOTSUP') }}}: 'Not supported', + {{{ cDefine('ENOMEDIUM') }}}: 'No medium (in tape drive)', + {{{ cDefine('ENOSHARE') }}}: 'No such host or network path', + {{{ cDefine('ECASECLASH') }}}: 'Filename exists with different case', + {{{ cDefine('EILSEQ') }}}: 'EILSEQ', + {{{ cDefine('EOVERFLOW') }}}: 'Value too large for defined data type', + {{{ cDefine('ECANCELED') }}}: 'Operation canceled', + {{{ cDefine('ENOTRECOVERABLE') }}}: 'State not recoverable', + {{{ cDefine('EOWNERDEAD') }}}: 'Previous owner died', + {{{ cDefine('ESTRPIPE') }}}: 'Streams pipe error', }, __errno_state: 0, __setErrNo__deps: ['__errno_state'], diff --git a/src/settings.js b/src/settings.js index f620edf79dec8..21745ef83ac59 100644 --- a/src/settings.js +++ b/src/settings.js @@ -1322,6 +1322,133 @@ var C_DEFINES = { 'MAP_TYPE': '0x0f', 'MAP_FIXED': '0x100', 'MAP_ANONYMOUS': '0x10', - 'O_NOFOLLOW': '0200000' + 'O_NOFOLLOW': '0200000', + 'EPERM': '1', + 'ENOENT': '2', + 'ESRCH': '3', + 'EINTR': '4', + 'EIO': '5', + 'ENXIO': '6', + 'E2BIG': '7', + 'ENOEXEC': '8', + 'EBADF': '9', + 'ECHILD': '10', + 'EAGAIN': '11', + 'EWOULDBLOCK': '11', + 'ENOMEM': '12', + 'EACCES': '13', + 'EFAULT': '14', + 'ENOTBLK': '15', + 'EBUSY': '16', + 'EEXIST': '17', + 'EXDEV': '18', + 'ENODEV': '19', + 'ENOTDIR': '20', + 'EISDIR': '21', + 'EINVAL': '22', + 'ENFILE': '23', + 'EMFILE': '24', + 'ENOTTY': '25', + 'ETXTBSY': '26', + 'EFBIG': '27', + 'ENOSPC': '28', + 'ESPIPE': '29', + 'EROFS': '30', + 'EMLINK': '31', + 'EPIPE': '32', + 'EDOM': '33', + 'ERANGE': '34', + 'ENOMSG': '35', + 'EIDRM': '36', + 'ECHRNG': '37', + 'EL2NSYNC': '38', + 'EL3HLT': '39', + 'EL3RST': '40', + 'ELNRNG': '41', + 'EUNATCH': '42', + 'ENOCSI': '43', + 'EL2HLT': '44', + 'EDEADLK': '45', + 'ENOLCK': '46', + 'EBADE': '50', + 'EBADR': '51', + 'EXFULL': '52', + 'ENOANO': '53', + 'EBADRQC': '54', + 'EBADSLT': '55', + 'EDEADLOCK': '56', + 'EBFONT': '57', + 'ENOSTR': '60', + 'ENODATA': '61', + 'ETIME': '62', + 'ENOSR': '63', + 'ENONET': '64', + 'ENOPKG': '65', + 'EREMOTE': '66', + 'ENOLINK': '67', + 'EADV': '68', + 'ESRMNT': '69', + 'ECOMM': '70', + 'EPROTO': '71', + 'EMULTIHOP': '74', + 'ELBIN': '75', + 'EDOTDOT': '76', + 'EBADMSG': '77', + 'EFTYPE': '79', + 'ENOTUNIQ': '80', + 'EBADFD': '81', + 'EREMCHG': '82', + 'ELIBACC': '83', + 'ELIBBAD': '84', + 'ELIBSCN': '85', + 'ELIBMAX': '86', + 'ELIBEXEC': '87', + 'ENOSYS': '88', + 'ENMFILE': '89', + 'ENOTEMPTY': '90', + 'ENAMETOOLONG': '91', + 'ELOOP': '92', + 'EOPNOTSUPP': '95', + 'EPFNOSUPPORT': '96', + 'ECONNRESET': '104', + 'ENOBUFS': '105', + 'EAFNOSUPPORT': '106', + 'EPROTOTYPE': '107', + 'ENOTSOCK': '108', + 'ENOPROTOOPT': '109', + 'ESHUTDOWN': '110', + 'ECONNREFUSED': '111', + 'EADDRINUSE': '112', + 'ECONNABORTED': '113', + 'ENETUNREACH': '114', + 'ENETDOWN': '115', + 'ETIMEDOUT': '116', + 'EHOSTDOWN': '117', + 'EHOSTUNREACH': '118', + 'EINPROGRESS': '119', + 'EALREADY': '120', + 'EDESTADDRREQ': '121', + 'EMSGSIZE': '122', + 'EPROTONOSUPPORT': '123', + 'ESOCKTNOSUPPORT': '124', + 'EADDRNOTAVAIL': '125', + 'ENETRESET': '126', + 'EISCONN': '127', + 'ENOTCONN': '128', + 'ETOOMANYREFS': '129', + 'EPROCLIM': '130', + 'EUSERS': '131', + 'EDQUOT': '132', + 'ESTALE': '133', + 'ENOTSUP': '134', + 'ENOMEDIUM': '135', + 'ENOSHARE': '136', + 'ECASECLASH': '137', + 'EILSEQ': '138', + 'EOVERFLOW': '139', + 'ECANCELED': '140', + 'ENOTRECOVERABLE': '141', + 'EOWNERDEAD': '142', + 'ESTRPIPE': '143' }; From db911f46e14421b6f7807ab3179d3b078212347b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jukka=20Jyl=C3=A4nki?= Date: Fri, 9 Aug 2013 13:58:50 +0300 Subject: [PATCH 008/112] Fix file packager to generate a virtual filesystem with '/' as path separators when the host is Windows. --- tools/file_packager.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/file_packager.py b/tools/file_packager.py index 9861e330f06b0..33ccebad82750 100644 --- a/tools/file_packager.py +++ b/tools/file_packager.py @@ -40,7 +40,7 @@ ''' import os, sys, shutil, random, uuid, ctypes - +import posixpath import shared from shared import Compression, execute, suffix, unsuffixed from subprocess import Popen, PIPE, STDOUT @@ -217,7 +217,7 @@ def add(arg, dirname, names): if file_['dstpath'].endswith('/'): # If user has submitted a directory name as the destination but omitted the destination filename, use the filename from source file file_['dstpath'] = file_['dstpath'] + os.path.basename(file_['srcpath']) # make destination path always relative to the root - file_['dstpath'] = os.path.normpath(os.path.join('/', file_['dstpath'])) + file_['dstpath'] = posixpath.normpath(os.path.join('/', file_['dstpath'])) if DEBUG: print >> sys.stderr, 'Packaging file "' + file_['srcpath'] + '" to VFS in path "' + file_['dstpath'] + '".' From 4c8be1a462006db29ade93dc5093aab5374e56d8 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 9 Aug 2013 10:14:37 -0700 Subject: [PATCH 009/112] document browser sockets test runner option --- tests/runner.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/runner.py b/tests/runner.py index 67b812140ba8c..096f3426093bb 100755 --- a/tests/runner.py +++ b/tests/runner.py @@ -27,6 +27,10 @@ benchmark - run before and after each set of changes before pushing to master, verify no regressions browser - runs pages in a web browser + +There are also commands to run specific subsets of the test suite: + + browser sockets - runs websocket networking tests browser audio - runs audio tests in a web browser (requires human verification) To run one of those parts, do something like From a9bb2f0261fd88048d7e499df33808a23884cb02 Mon Sep 17 00:00:00 2001 From: Anthony Pesch Date: Fri, 9 Aug 2013 00:36:35 -0700 Subject: [PATCH 010/112] - always throw an exception in exit - remove default exit status prints - added EXITSTATUS global to enable exit callbacks to determine the status --- src/postamble.js | 60 ++++++++++++++++++++++-------------------------- src/preamble.js | 1 + tests/runner.py | 30 ++++++++++++++++++++++-- 3 files changed, 57 insertions(+), 34 deletions(-) diff --git a/src/postamble.js b/src/postamble.js index 25a50bfc38b86..ad265b32d1837 100644 --- a/src/postamble.js +++ b/src/postamble.js @@ -2,7 +2,6 @@ // === Auto-generated postamble setup entry stuff === var initialStackTop; -var inMain; Module['callMain'] = Module.callMain = function callMain(args) { assert(runDependencies == 0, 'cannot call main when async dependencies remain! (listen on __ATMAIN__)'); @@ -27,40 +26,36 @@ Module['callMain'] = Module.callMain = function callMain(args) { argv.push(0); argv = allocate(argv, 'i32', ALLOC_NORMAL); + initialStackTop = STACKTOP; + + try { #if BENCHMARK - var start = Date.now(); + var start = Date.now(); #endif - initialStackTop = STACKTOP; - inMain = true; + var ret = Module['_main'](argc, argv, 0); - var ret; - try { - ret = Module['_main'](argc, argv, 0); +#if BENCHMARK + Module.realPrint('main() took ' + (Date.now() - start) + ' milliseconds'); +#endif + + // if we're not running an evented main loop, it's time to exit + if (!Module['noExitRuntime']) { + exit(ret); + } } catch(e) { - if (e && typeof e == 'object' && e.type == 'ExitStatus') { + if (e.name == 'ExitStatus') { // exit() throws this once it's done to make sure execution // has been stopped completely - Module.print('Exit Status: ' + e.value); - return e.value; + return; } else if (e == 'SimulateInfiniteLoop') { // running an evented main loop, don't immediately exit Module['noExitRuntime'] = true; + return; } else { throw e; } - } finally { - inMain = false; - } - -#if BENCHMARK - Module.realPrint('main() took ' + (Date.now() - start) + ' milliseconds'); -#endif - - // if we're not running an evented main loop, it's time to exit - if (!Module['noExitRuntime']) { - exit(ret); } } @@ -110,21 +105,21 @@ Module['run'] = Module.run = run; function exit(status) { ABORT = true; + EXITSTATUS = status; STACKTOP = initialStackTop; - // TODO call externally added 'exit' callbacks with the status code. - // It'd be nice to provide the same interface for all Module events (e.g. - // prerun, premain, postmain). Perhaps an EventEmitter so we can do: - // Module.on('exit', function (status) {}); - // exit the runtime exitRuntime(); - - if (inMain) { - // if we're still inside the callMain's try/catch, we need to throw an - // exception in order to immediately terminate execution. - throw { type: 'ExitStatus', value: status }; - } + + // throw an exception to halt the current execution + function ExitStatus() { + this.name = "ExitStatus"; + this.message = "Program terminated with exit(" + status + ")"; + this.status = status; + }; + ExitStatus.prototype = new Error(); + ExitStatus.prototype.constructor = ExitStatus; + throw new ExitStatus(); } Module['exit'] = Module.exit = exit; @@ -134,6 +129,7 @@ function abort(text) { } ABORT = true; + EXITSTATUS = 1; throw 'abort() at ' + (new Error().stack); } diff --git a/src/preamble.js b/src/preamble.js index 585db83290840..95bf2dc252062 100644 --- a/src/preamble.js +++ b/src/preamble.js @@ -241,6 +241,7 @@ var setjmpLabels = {}; #endif var ABORT = false; // whether we are quitting the application. no code should run after this. set in exit() and abort() +var EXITSTATUS = 0; var undef = 0; // tempInt is used for 32-bit signed values or smaller. tempBigInt is used diff --git a/tests/runner.py b/tests/runner.py index e77efffbdf2b0..8122bc81ebb68 100755 --- a/tests/runner.py +++ b/tests/runner.py @@ -3185,6 +3185,25 @@ def test_std_exception(self): ''' self.do_run(src, 'caught std::exception') + def test_async_exit(self): + open('main.c', 'w').write(r''' + #include + #include + #include "emscripten.h" + + void main_loop() { + exit(EXIT_SUCCESS); + } + + int main() { + emscripten_set_main_loop(main_loop, 60, 0); + return 0; + } + ''') + + Popen([PYTHON, EMCC, 'main.c']).communicate() + self.assertNotContained('Reached an unreachable!', run_js(self.in_dir('a.out.js'), stderr=STDOUT)) + def test_exit_stack(self): if self.emcc_args is None: return self.skip('requires emcc') if Settings.ASM_JS: return self.skip('uses report_stack without exporting') @@ -3222,6 +3241,7 @@ def test_exit_stack(self): } var Module = { postRun: function() { + Module.print('Exit Status: ' + EXITSTATUS); Module.print('postRun'); assert(initialStack == STACKTOP, [initialStack, STACKTOP]); Module.print('ok.'); @@ -10219,13 +10239,19 @@ def test_exit_status(self): printf("cleanup\n"); } - int main() - { + int main() { atexit(cleanup); // this atexit should still be called printf("hello, world!\n"); exit(118); // Unusual exit status to make sure it's working! } ''' + open('post.js', 'w').write(''' + Module.addOnExit(function () { + Module.print('Exit Status: ' + EXITSTATUS); + }); + Module.callMain(); + ''') + self.emcc_args += ['-s', 'INVOKE_RUN=0', '--post-js', 'post.js'] self.do_run(src, 'hello, world!\ncleanup\nExit Status: 118') def test_gc(self): From 4776303a18c49351ce151103629d32dca8949fb1 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 9 Aug 2013 11:46:59 -0700 Subject: [PATCH 011/112] make range testing more flexible in test_outline --- tests/runner.py | 43 +++++++++++++++++++++++++++---------------- 1 file changed, 27 insertions(+), 16 deletions(-) diff --git a/tests/runner.py b/tests/runner.py index 096f3426093bb..4f2f024ee331b 100755 --- a/tests/runner.py +++ b/tests/runner.py @@ -11229,7 +11229,7 @@ def test(name, header, main, side, expected, args=[], suffix='cpp', first=True): def test_outline(self): - def test(name, src, libs, expected, expected_ranges, args=[], suffix='cpp', test_sizes=True): + def test(name, src, libs, expected, expected_ranges, args=[], suffix='cpp'): print name def measure_funcs(filename): @@ -11255,7 +11255,7 @@ def measure_funcs(filename): (['-g'], (100, 250, 500, 1000, 2000, 5000, 0)) ]: for outlining_limit in outlining_limits: - print '\n', debug, outlining_limit, '\n' + print '\n', Building.COMPILER_TEST_OPTS, debug, outlining_limit, '\n' # TODO: test without -g3, tell all sorts Popen([PYTHON, EMCC, src] + libs + ['-o', 'test.js', '-O2'] + debug + ['-s', 'OUTLINING_LIMIT=%d' % outlining_limit] + args).communicate() assert os.path.exists('test.js') @@ -11268,24 +11268,35 @@ def measure_funcs(filename): low = expected_ranges[outlining_limit][0] seen = max(measure_funcs('test.js').values()) high = expected_ranges[outlining_limit][1] - print outlining_limit, ' ', low, '<=', seen, '<=', high - if test_sizes: assert low <= seen <= high - - for test_opts, test_sizes in [([], True), (['-O2'], False)]: + print Building.COMPILER_TEST_OPTS, outlining_limit, ' ', low, '<=', seen, '<=', high + assert low <= seen <= high + + for test_opts, expected_ranges in [ + ([], { + 100: (190, 250), + 250: (200, 330), + 500: (250, 310), + 1000: (230, 300), + 2000: (380, 450), + 5000: (800, 1100), + 0: (1500, 1800) + }), + (['-O2'], { + 100: (0, 1500), + 250: (0, 1500), + 500: (0, 1500), + 1000: (0, 1500), + 2000: (0, 2000), + 5000: (0, 5000), + 0: (0, 5000) + }), + ]: Building.COMPILER_TEST_OPTS = test_opts test('zlib', path_from_root('tests', 'zlib', 'example.c'), self.get_library('zlib', os.path.join('libz.a'), make_args=['libz.a']), open(path_from_root('tests', 'zlib', 'ref.txt'), 'r').read(), - { - 100: (190, 250), - 250: (200, 330), - 500: (250, 310), - 1000: (230, 300), - 2000: (380, 450), - 5000: (800, 1100), - 0: (1500, 1800) - }, - args=['-I' + path_from_root('tests', 'zlib')], suffix='c', test_sizes=test_sizes) + expected_ranges, + args=['-I' + path_from_root('tests', 'zlib')], suffix='c') def test_symlink(self): if os.name == 'nt': From 39cf1e36ab14c621f1e446010afbec21a8b10dbb Mon Sep 17 00:00:00 2001 From: Anthony Pesch Date: Fri, 9 Aug 2013 12:29:41 -0700 Subject: [PATCH 012/112] use instanceof operator for detecting ExitStatus throw --- src/postamble.js | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/postamble.js b/src/postamble.js index ad265b32d1837..c4ca3aae686ca 100644 --- a/src/postamble.js +++ b/src/postamble.js @@ -1,6 +1,14 @@ // === Auto-generated postamble setup entry stuff === +function ExitStatus(status) { + this.name = "ExitStatus"; + this.message = "Program terminated with exit(" + status + ")"; + this.status = status; +}; +ExitStatus.prototype = new Error(); +ExitStatus.prototype.constructor = ExitStatus; + var initialStackTop; Module['callMain'] = Module.callMain = function callMain(args) { @@ -45,7 +53,7 @@ Module['callMain'] = Module.callMain = function callMain(args) { } } catch(e) { - if (e.name == 'ExitStatus') { + if (e instanceof ExitStatus) { // exit() throws this once it's done to make sure execution // has been stopped completely return; @@ -112,14 +120,7 @@ function exit(status) { exitRuntime(); // throw an exception to halt the current execution - function ExitStatus() { - this.name = "ExitStatus"; - this.message = "Program terminated with exit(" + status + ")"; - this.status = status; - }; - ExitStatus.prototype = new Error(); - ExitStatus.prototype.constructor = ExitStatus; - throw new ExitStatus(); + throw new ExitStatus(status); } Module['exit'] = Module.exit = exit; From b2c5250d4fd96387533724f6c48f4eec1400f3b5 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 9 Aug 2013 13:12:10 -0700 Subject: [PATCH 013/112] --js-opts option to turn off js optimizer --- emcc | 37 ++++++++++++++++++++++++------------- tests/runner.py | 1 + 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/emcc b/emcc index 0eb4499bdf6c5..b63eada00079c 100755 --- a/emcc +++ b/emcc @@ -219,6 +219,9 @@ Options that are modified or new in %s include: 1: Parallel typed arrays 2: Shared (C-like) typed arrays (default) + --js-opts 0: Prevent JS optimizer from running + 1: Use JS optimizer (default) + --llvm-opts 0: No LLVM optimizations (default in -O0) 1: -O1 LLVM optimizations (default in -O1) 2: -O2 LLVM optimizations @@ -712,6 +715,7 @@ try: opt_level = 0 debug_level = 0 + js_opts = None llvm_opts = None llvm_lto = None closure = None @@ -767,6 +771,11 @@ try: settings_changes.append('INLINING_LIMIT=50') opt_level = validate_arg_level(requested_level, 3, 'Invalid optimization level: ' + newargs[i]) newargs[i] = '' + elif newargs[i].startswith('--js-opts'): + check_bad_eq(newargs[i]) + js_opts = eval(newargs[i+1]) + newargs[i] = '' + newargs[i+1] = '' elif newargs[i].startswith('--llvm-opts'): check_bad_eq(newargs[i]) llvm_opts = eval(newargs[i+1]) @@ -899,6 +908,7 @@ try: if default_cxx_std: newargs = newargs + [default_cxx_std] + if js_opts is None: js_opts = True if llvm_opts is None: llvm_opts = LLVM_OPT_LEVEL[opt_level] if llvm_lto is None and opt_level >= 3: llvm_lto = 3 if opt_level == 0: debug_level = 4 @@ -1594,7 +1604,7 @@ try: js_optimizer_queue = [] js_optimizer_extra_info = {} - if opt_level >= 1: + if opt_level >= 1 and js_opts: logging.debug('running pre-closure post-opts') if DEBUG == '2': @@ -1623,23 +1633,24 @@ try: final = shared.Building.closure_compiler(final) if DEBUG: save_intermediate('closure') - if shared.Settings.OUTLINING_LIMIT > 0: - js_optimizer_queue += ['outline'] - js_optimizer_extra_info['sizeToOutline'] = shared.Settings.OUTLINING_LIMIT + if js_opts: + if shared.Settings.OUTLINING_LIMIT > 0: + js_optimizer_queue += ['outline'] + js_optimizer_extra_info['sizeToOutline'] = shared.Settings.OUTLINING_LIMIT - if (not closure or shared.Settings.ASM_JS) and shared.Settings.RELOOP and debug_level < 3: - js_optimizer_queue += ['registerize'] + if (not closure or shared.Settings.ASM_JS) and shared.Settings.RELOOP and debug_level < 3: + js_optimizer_queue += ['registerize'] - if opt_level > 0: - if debug_level < 2 and shared.Settings.ASM_JS: js_optimizer_queue = map(lambda p: p if p != 'registerize' else 'registerizeAndMinify', js_optimizer_queue) - if debug_level == 0: js_optimizer_queue += ['minifyWhitespace'] + if opt_level > 0: + if debug_level < 2 and shared.Settings.ASM_JS: js_optimizer_queue = map(lambda p: p if p != 'registerize' else 'registerizeAndMinify', js_optimizer_queue) + if debug_level == 0: js_optimizer_queue += ['minifyWhitespace'] - if closure and shared.Settings.ASM_JS: - js_optimizer_queue += ['closure'] + if closure and shared.Settings.ASM_JS: + js_optimizer_queue += ['closure'] - if not shared.Settings.SIDE_MODULE: js_optimizer_queue += ['last'] # side modules are not finalized until after relocation + if not shared.Settings.SIDE_MODULE: js_optimizer_queue += ['last'] # side modules are not finalized until after relocation - flush_js_optimizer_queue() + flush_js_optimizer_queue() # Remove some trivial whitespace # TODO: do not run when compress has already been done on all parts of the code src = open(final).read() diff --git a/tests/runner.py b/tests/runner.py index bd98fe6370343..d35e8750e24ce 100755 --- a/tests/runner.py +++ b/tests/runner.py @@ -10682,6 +10682,7 @@ def test_emcc(self): (['-O2', '--closure', '1'], lambda generated: 'function intArrayToString' not in generated, 'closure minifies the shell'), (['-O2'], lambda generated: 'var b=0' in generated and not 'function _main' in generated, 'registerize/minify is run by default in -O2'), (['-O2', '--minify', '0'], lambda generated: 'var b = 0' in generated and not 'function _main' in generated, 'minify is cancelled, but not registerize'), + (['-O2', '--js-opts', '0'], lambda generated: 'var b=0' not in generated and 'var b = 0' not in generated and 'function _main' in generated, 'js opts are cancelled'), (['-O2', '-g'], lambda generated: 'var b=0' not in generated and 'var b = 0' not in generated and 'function _main' in generated, 'registerize/minify is cancelled by -g'), (['-O2', '-g0'], lambda generated: 'var b=0' in generated and not 'function _main' in generated, 'registerize/minify is run by default in -O2 -g0'), (['-O2', '-g1'], lambda generated: 'var b = 0' in generated and not 'function _main' in generated, 'compress is cancelled by -g1'), From 12700c43d60eaae9101cf307a25a211886add1c6 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 9 Aug 2013 13:37:48 -0700 Subject: [PATCH 014/112] print from exit(), so that explicitly calling it prints out the exit status --- src/library.js | 1 + tests/runner.py | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/library.js b/src/library.js index b68a9324d3514..f0302aaa8f56f 100644 --- a/src/library.js +++ b/src/library.js @@ -2961,6 +2961,7 @@ LibraryManager.library = { _exit: function(status) { // void _exit(int status); // http://pubs.opengroup.org/onlinepubs/000095399/functions/exit.html + Module.print('exit(' + status + ') called'); Module['exit'](status); }, fork__deps: ['__setErrNo', '$ERRNO_CODES'], diff --git a/tests/runner.py b/tests/runner.py index d35e8750e24ce..8ab3609b3c315 100755 --- a/tests/runner.py +++ b/tests/runner.py @@ -10236,6 +10236,7 @@ def test_linespecific(self): Settings.CORRECT_SIGNS = 0 def test_exit_status(self): + if self.emcc_args is None: return self.skip('need emcc') src = r''' #include #include @@ -10251,12 +10252,12 @@ def test_exit_status(self): ''' open('post.js', 'w').write(''' Module.addOnExit(function () { - Module.print('Exit Status: ' + EXITSTATUS); + Module.print('I see exit status: ' + EXITSTATUS); }); Module.callMain(); ''') self.emcc_args += ['-s', 'INVOKE_RUN=0', '--post-js', 'post.js'] - self.do_run(src, 'hello, world!\ncleanup\nExit Status: 118') + self.do_run(src, 'hello, world!\nexit(118) called\ncleanup\nI see exit status: 118') def test_gc(self): if self.emcc_args == None: return self.skip('needs ta2') From 4ae35bd2e1fe6e4fedffe90019f1ef6b06fa15c7 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 9 Aug 2013 14:02:40 -0700 Subject: [PATCH 015/112] use a local variable for varargs temp stack position --- src/jsifier.js | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/jsifier.js b/src/jsifier.js index b377202d91da6..1d70fd6fee174 100644 --- a/src/jsifier.js +++ b/src/jsifier.js @@ -658,6 +658,10 @@ function JSify(data, functionsOnly, givenFunctions) { } } + if (func.hasVarArgsCall) { + func.JS += INDENTATION + 'var tempVarArgs = 0;\n'; + } + // Prepare the stack, if we need one. If we have other stack allocations, force the stack to be set up. func.JS += INDENTATION + RuntimeGenerator.stackEnter(func.initialStack, func.otherStackAllocations) + ';\n'; @@ -1446,12 +1450,13 @@ function JSify(data, functionsOnly, givenFunctions) { }); if (hasVarArgs && !useJSArgs) { + funcData.hasVarArgsCall = true; if (varargs.length === 0) { varargs = [0]; varargsTypes = ['i32']; } var offset = 0; - varargs = '(tempInt=' + RuntimeGenerator.stackAlloc(varargs.length, ',') + ',' + + varargs = '(tempVarArgs=' + RuntimeGenerator.stackAlloc(varargs.length, ',') + ',' + varargs.map(function(arg, i) { var type = varargsTypes[i]; if (type == 0) return null; @@ -1459,17 +1464,17 @@ function JSify(data, functionsOnly, givenFunctions) { var ret; assert(offset % Runtime.STACK_ALIGN == 0); // varargs must be aligned if (!varargsByVals[i]) { - ret = makeSetValue(getFastValue('tempInt', '+', offset), 0, arg, type, null, null, Runtime.STACK_ALIGN, null, ','); + ret = makeSetValue(getFastValue('tempVarArgs', '+', offset), 0, arg, type, null, null, Runtime.STACK_ALIGN, null, ','); offset += Runtime.alignMemory(Runtime.getNativeFieldSize(type), Runtime.STACK_ALIGN); } else { var size = calcAllocatedSize(removeAllPointing(type)); - ret = makeCopyValues(getFastValue('tempInt', '+', offset), arg, size, null, null, varargsByVals[i], ','); + ret = makeCopyValues(getFastValue('tempVarArgs', '+', offset), arg, size, null, null, varargsByVals[i], ','); offset += Runtime.forceAlign(size, Runtime.STACK_ALIGN); } return ret; }).filter(function(arg) { return arg !== null; - }).join(',') + ',tempInt)'; + }).join(',') + ',tempVarArgs)'; varargs = asmCoercion(varargs, 'i32'); } From 11dcd5afe75a5079c01c1c3e1126d6da433a2967 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 9 Aug 2013 14:27:10 -0700 Subject: [PATCH 016/112] clean up stack space allocated by a varargs function call immediately after it; fixes #1492 --- src/jsifier.js | 17 ++++++++++- tests/runner.py | 76 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+), 1 deletion(-) diff --git a/src/jsifier.js b/src/jsifier.js index 1d70fd6fee174..c92526d27f8a0 100644 --- a/src/jsifier.js +++ b/src/jsifier.js @@ -1238,6 +1238,7 @@ function JSify(data, functionsOnly, givenFunctions) { + (EXCEPTION_DEBUG ? 'Module.print("Exception: " + e + ", currently at: " + (new Error().stack)); ' : '') + 'return null } })();'; } + ret = makeVarArgsCleanup(ret); if (item.assignTo) { ret = 'var ' + item.assignTo + ' = ' + ret; @@ -1562,10 +1563,24 @@ function JSify(data, functionsOnly, givenFunctions) { return ret; } + + function makeVarArgsCleanup(js) { + if (js.indexOf('(tempVarArgs=') >= 0) { + if (js[js.length-1] == ';') { + return js + ' STACKTOP=tempVarArgs;'; + } else { + assert(js.indexOf(';') < 0); + return '((' + js + '), STACKTOP=tempVarArgs)'; + } + } + return js; + } + makeFuncLineActor('getelementptr', function(item) { return finalizeLLVMFunctionCall(item) }); makeFuncLineActor('call', function(item) { if (item.standalone && LibraryManager.isStubFunction(item.ident)) return ';'; - return makeFunctionCall(item.ident, item.params, item.funcData, item.type, false, !!item.assignTo || !item.standalone) + (item.standalone ? ';' : ''); + var ret = makeFunctionCall(item.ident, item.params, item.funcData, item.type, false, !!item.assignTo || !item.standalone) + (item.standalone ? ';' : ''); + return makeVarArgsCleanup(ret); }); makeFuncLineActor('unreachable', function(item) { diff --git a/tests/runner.py b/tests/runner.py index 8ab3609b3c315..cbdacc2cdfaad 100755 --- a/tests/runner.py +++ b/tests/runner.py @@ -3719,6 +3719,82 @@ def test_stack_varargs(self): Settings.TOTAL_STACK = 1024 self.do_run(src, 'ok!') + def test_stack_varargs2(self): + if self.emcc_args is None: return # too slow in other modes + Settings.TOTAL_STACK = 1024 + src = r''' + #include + #include + + void func(int i) { + } + int main() { + for (int i = 0; i < 1024; 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); + } + printf("ok!\n"); + return 0; + } + ''' + self.do_run(src, 'ok!') + + print 'with return' + + src = r''' + #include + #include + + int main() { + for (int i = 0; i < 1024; i++) { + int j = 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", + 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); + printf(" (%d)\n", j); + } + printf("ok!\n"); + return 0; + } + ''' + self.do_run(src, 'ok!') + + print 'with definitely no return' + + src = r''' + #include + #include + #include + + void vary(const char *s, ...) + { + va_list v; + va_start(v, s); + char d[20]; + vsnprintf(d, 20, s, v); + puts(d); + + // Try it with copying + va_list tempva; + va_copy(tempva, v); + vsnprintf(d, 20, s, tempva); + puts(d); + + va_end(v); + } + + int main() { + for (int i = 0; i < 1024; i++) { + int j = 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", + 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); + printf(" (%d)\n", j); + vary("*cheez: %d+%d*", 99, 24); + vary("*albeit*"); + } + printf("ok!\n"); + return 0; + } + ''' + self.do_run(src, 'ok!') + def test_stack_void(self): Settings.INLINING_LIMIT = 50 From 78cc888615b08ee97d30db7c5cf07ce4f92be398 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 9 Aug 2013 15:26:17 -0700 Subject: [PATCH 017/112] report more variable stats when outlining --- tools/js-optimizer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js index 4192ddd164d37..79741d877642a 100644 --- a/tools/js-optimizer.js +++ b/tools/js-optimizer.js @@ -3436,7 +3436,7 @@ function outline(ast) { } } outliningParents[newIdent] = func[1]; - printErr('performed outline ' + [func[1], newIdent, 'code sizes (pre/post):', originalCodeSize, measureSize(code), 'overhead (w/r):', setSize(setSub(codeInfo.writes, owned)), setSize(setSub(codeInfo.reads, owned))]); + printErr('performed outline ' + [func[1], newIdent, 'code sizes (pre/post):', originalCodeSize, measureSize(code), 'overhead (w/r):', setSize(setSub(codeInfo.writes, owned)), setSize(setSub(codeInfo.reads, owned)), ' owned: ', setSize(owned), ' left: ', setSize(asmData.vars), setSize(asmData.params)]); calculateThreshold(func); return [newFunc]; } From 459b3a24ec90f474bd491c860247d6193cbef329 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 9 Aug 2013 15:55:06 -0700 Subject: [PATCH 018/112] track loop depth in outliner --- tools/js-optimizer.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js index 79741d877642a..3e7926b71f556 100644 --- a/tools/js-optimizer.js +++ b/tools/js-optimizer.js @@ -3220,7 +3220,7 @@ function outline(ast) { printErr('trying to reduce the size of ' + func[1] + ' which is ' + size + ' (>= ' + extraInfo.sizeToOutline + '), aim for ' + sizeToOutline); } - var level = 0; + var level = 0, loops = 0; var outliningParents = {}; // function name => parent it was outlined from function doOutline(func, asmData, stats, start, end) { @@ -3436,7 +3436,7 @@ function outline(ast) { } } outliningParents[newIdent] = func[1]; - printErr('performed outline ' + [func[1], newIdent, 'code sizes (pre/post):', originalCodeSize, measureSize(code), 'overhead (w/r):', setSize(setSub(codeInfo.writes, owned)), setSize(setSub(codeInfo.reads, owned)), ' owned: ', setSize(owned), ' left: ', setSize(asmData.vars), setSize(asmData.params)]); + printErr('performed outline ' + [func[1], newIdent, 'code sizes (pre/post):', originalCodeSize, measureSize(code), 'overhead (w/r):', setSize(setSub(codeInfo.writes, owned)), setSize(setSub(codeInfo.reads, owned)), ' owned: ', setSize(owned), ' left: ', setSize(asmData.vars), setSize(asmData.params), ' loopsDepth: ', loops]); calculateThreshold(func); return [newFunc]; } @@ -3506,6 +3506,12 @@ function outline(ast) { if (subRet && subRet.length > 0) ret.push.apply(ret, subRet); } return null; // do not recurse into children, outlineStatements will do so if necessary + } else if (type == 'while') { + loops++; + } + }, function(node, type) { + if (type == 'while') { + loops--; } }); if (ret.length > pre) { From 852bfa71be2246d74f3c7ca456321b174ff2d6d9 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 9 Aug 2013 16:30:13 -0700 Subject: [PATCH 019/112] sort spills and unspills in outliner --- tools/js-optimizer.js | 29 ++++--- .../test-js-optimizer-asm-outline1-output.js | 84 +++++++++---------- .../test-js-optimizer-asm-outline2-output.js | 66 +++++++-------- 3 files changed, 94 insertions(+), 85 deletions(-) diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js index 3e7926b71f556..5f1c6df86a9c5 100644 --- a/tools/js-optimizer.js +++ b/tools/js-optimizer.js @@ -3241,21 +3241,29 @@ function outline(ast) { } }); var reps = []; - // wipe out control variable - reps.push(['stat', makeAssign(makeStackAccess(ASM_INT, asmData.controlStackPos(outlineIndex)), ['num', 0])]); - reps.push(['stat', makeAssign(makeStackAccess(ASM_INT, asmData.controlDataStackPos(outlineIndex)), ['num', 0])]); // XXX not really needed - // add spills and reads before and after the call to the outlined code, and in the outlined code itself - keys(setUnion(codeInfo.reads, codeInfo.writes)).forEach(function(v) { + // add spills + function orderFunc(x, y) { + return (asmData.stackPos[x] - asmData.stackPos[y]) || x.localeCompare(y); + } + var sortedReadsAndWrites = keys(setUnion(codeInfo.reads, codeInfo.writes)).sort(orderFunc); + var sortedWrites = keys(codeInfo.writes).sort(orderFunc); + sortedReadsAndWrites.forEach(function(v) { if (!(v in owned)) { reps.push(['stat', ['assign', true, ['sub', ['name', getAsmType(v, asmData) == ASM_INT ? 'HEAP32' : 'HEAPF32'], ['binary', '>>', ['binary', '+', ['name', 'sp'], ['num', asmData.stackPos[v]]], ['num', '2']]], ['name', v]]]); } }); + // wipe out control variable + reps.push(['stat', makeAssign(makeStackAccess(ASM_INT, asmData.controlStackPos(outlineIndex)), ['num', 0])]); + reps.push(['stat', makeAssign(makeStackAccess(ASM_INT, asmData.controlDataStackPos(outlineIndex)), ['num', 0])]); // XXX not really needed + // do the call reps.push(['stat', ['call', ['name', newIdent], [['name', 'sp']]]]); - for (var v in codeInfo.writes) { + // add unspills + sortedWrites.forEach(function(v) { if (!(v in owned)) { reps.push(['stat', ['assign', true, ['name', v], makeAsmCoercion(['sub', ['name', getAsmType(v, asmData) == ASM_INT ? 'HEAP32' : 'HEAPF32'], ['binary', '>>', ['binary', '+', ['name', 'sp'], ['num', asmData.stackPos[v]]], ['num', '2']]], getAsmType(v, asmData))]]); } - } + }); + // Generate new function if (codeInfo.hasReturn || codeInfo.hasReturnInt || codeInfo.hasReturnDouble || codeInfo.hasBreak || codeInfo.hasContinue) { // we need to capture all control flow using a top-level labeled one-time loop in the outlined function @@ -3389,16 +3397,17 @@ function outline(ast) { } } // add spills and unspills in outlined code outside the OL loop - keys(setUnion(codeInfo.reads, codeInfo.writes)).forEach(function(v) { + sortedReadsAndWrites.reverse(); + sortedReadsAndWrites.forEach(function(v) { if (!(v in owned)) { code.unshift(['stat', ['assign', true, ['name', v], makeAsmCoercion(['sub', ['name', getAsmType(v, asmData) == ASM_INT ? 'HEAP32' : 'HEAPF32'], ['binary', '>>', ['binary', '+', ['name', 'sp'], ['num', asmData.stackPos[v]]], ['num', '2']]], getAsmType(v, asmData))]]); } }); - for (var v in codeInfo.writes) { + sortedWrites.forEach(function(v) { if (!(v in owned)) { code.push(['stat', ['assign', true, ['sub', ['name', getAsmType(v, asmData) == ASM_INT ? 'HEAP32' : 'HEAPF32'], ['binary', '>>', ['binary', '+', ['name', 'sp'], ['num', asmData.stackPos[v]]], ['num', '2']]], ['name', v]]]); } - } + }); // finalize var newFunc = ['defun', newIdent, ['sp'], code]; var newAsmData = { params: { sp: ASM_INT }, vars: {} }; diff --git a/tools/test-js-optimizer-asm-outline1-output.js b/tools/test-js-optimizer-asm-outline1-output.js index 5027f6801698e..de0747f9225ee 100644 --- a/tools/test-js-optimizer-asm-outline1-output.js +++ b/tools/test-js-optimizer-asm-outline1-output.js @@ -170,15 +170,15 @@ function vars(x, y) { var sp = 0; sp = STACKTOP; STACKTOP = STACKTOP + 152 | 0; + HEAP32[sp + 8 >> 2] = x; + HEAPF32[sp + 16 >> 2] = y; HEAP32[sp + 32 >> 2] = 0; HEAP32[sp + 36 >> 2] = 0; + vars$1(sp); HEAP32[sp + 8 >> 2] = x; HEAPF32[sp + 16 >> 2] = y; - vars$1(sp); HEAP32[sp + 24 >> 2] = 0; HEAP32[sp + 28 >> 2] = 0; - HEAP32[sp + 8 >> 2] = x; - HEAPF32[sp + 16 >> 2] = y; vars$0(sp); STACKTOP = sp; } @@ -192,10 +192,10 @@ function vars2(x, y) { b = y * x; a = c(1 + a); b = c(2 + b); - HEAP32[sp + 40 >> 2] = 0; - HEAP32[sp + 44 >> 2] = 0; HEAP32[sp + 24 >> 2] = a; HEAPF32[sp + 32 >> 2] = b; + HEAP32[sp + 40 >> 2] = 0; + HEAP32[sp + 44 >> 2] = 0; vars2$0(sp); a = HEAP32[sp + 24 >> 2] | 0; b = +HEAPF32[sp + 32 >> 2]; @@ -207,18 +207,18 @@ function vars3(x, y) { var a = 0, sp = 0; sp = STACKTOP; STACKTOP = STACKTOP + 160 | 0; - HEAP32[sp + 40 >> 2] = 0; - HEAP32[sp + 44 >> 2] = 0; - HEAP32[sp + 24 >> 2] = a; HEAP32[sp + 8 >> 2] = x; HEAPF32[sp + 16 >> 2] = y; + HEAP32[sp + 24 >> 2] = a; + HEAP32[sp + 40 >> 2] = 0; + HEAP32[sp + 44 >> 2] = 0; vars3$1(sp); a = HEAP32[sp + 24 >> 2] | 0; + HEAP32[sp + 8 >> 2] = x; + HEAPF32[sp + 16 >> 2] = y; + HEAP32[sp + 24 >> 2] = a; HEAP32[sp + 32 >> 2] = 0; HEAP32[sp + 36 >> 2] = 0; - HEAP32[sp + 24 >> 2] = a; - HEAPF32[sp + 16 >> 2] = y; - HEAP32[sp + 8 >> 2] = x; vars3$0(sp); a = HEAP32[sp + 24 >> 2] | 0; STACKTOP = sp; @@ -230,20 +230,20 @@ function vars4(x, y) { sp = STACKTOP; STACKTOP = STACKTOP + 168 | 0; a = x + y; - HEAP32[sp + 48 >> 2] = 0; - HEAP32[sp + 52 >> 2] = 0; - HEAPF32[sp + 16 >> 2] = y; HEAP32[sp + 8 >> 2] = x; + HEAPF32[sp + 16 >> 2] = y; HEAP32[sp + 24 >> 2] = a; HEAPF32[sp + 32 >> 2] = b; + HEAP32[sp + 48 >> 2] = 0; + HEAP32[sp + 52 >> 2] = 0; vars4$1(sp); - b = +HEAPF32[sp + 32 >> 2]; a = HEAP32[sp + 24 >> 2] | 0; - HEAP32[sp + 40 >> 2] = 0; - HEAP32[sp + 44 >> 2] = 0; - HEAP32[sp + 24 >> 2] = a; + b = +HEAPF32[sp + 32 >> 2]; HEAP32[sp + 8 >> 2] = x; + HEAP32[sp + 24 >> 2] = a; HEAPF32[sp + 32 >> 2] = b; + HEAP32[sp + 40 >> 2] = 0; + HEAP32[sp + 44 >> 2] = 0; vars4$0(sp); a = HEAP32[sp + 24 >> 2] | 0; b = +HEAPF32[sp + 32 >> 2]; @@ -256,19 +256,19 @@ function vars_w_stack(x, y) { sp = STACKTOP; STACKTOP = STACKTOP + 208 | 0; a = x + y; - HEAP32[sp + 72 >> 2] = 0; - HEAP32[sp + 76 >> 2] = 0; - HEAPF32[sp + 32 >> 2] = y; HEAP32[sp + 24 >> 2] = x; + HEAPF32[sp + 32 >> 2] = y; HEAP32[sp + 40 >> 2] = a; HEAPF32[sp + 48 >> 2] = b; + HEAP32[sp + 72 >> 2] = 0; + HEAP32[sp + 76 >> 2] = 0; vars_w_stack$1(sp); - b = +HEAPF32[sp + 48 >> 2]; a = HEAP32[sp + 40 >> 2] | 0; - HEAP32[sp + 64 >> 2] = 0; - HEAP32[sp + 68 >> 2] = 0; + b = +HEAPF32[sp + 48 >> 2]; HEAP32[sp + 40 >> 2] = a; HEAPF32[sp + 48 >> 2] = b; + HEAP32[sp + 64 >> 2] = 0; + HEAP32[sp + 68 >> 2] = 0; vars_w_stack$0(sp); a = HEAP32[sp + 40 >> 2] | 0; b = +HEAPF32[sp + 48 >> 2]; @@ -278,34 +278,34 @@ function chain() { sp = STACKTOP; STACKTOP = STACKTOP + 336 | 0; helper$0 = 1; + HEAP32[sp + 8 >> 2] = helper$0; HEAP32[sp + 56 >> 2] = 0; HEAP32[sp + 60 >> 2] = 0; - HEAP32[sp + 8 >> 2] = helper$0; chain$5(sp); helper$0 = HEAP32[sp + 8 >> 2] | 0; + HEAP32[sp + 8 >> 2] = helper$0; HEAP32[sp + 48 >> 2] = 0; HEAP32[sp + 52 >> 2] = 0; - HEAP32[sp + 8 >> 2] = helper$0; chain$4(sp); helper$0 = HEAP32[sp + 8 >> 2] | 0; + HEAP32[sp + 8 >> 2] = helper$0; HEAP32[sp + 40 >> 2] = 0; HEAP32[sp + 44 >> 2] = 0; - HEAP32[sp + 8 >> 2] = helper$0; chain$3(sp); helper$0 = HEAP32[sp + 8 >> 2] | 0; + HEAP32[sp + 8 >> 2] = helper$0; HEAP32[sp + 32 >> 2] = 0; HEAP32[sp + 36 >> 2] = 0; - HEAP32[sp + 8 >> 2] = helper$0; chain$2(sp); helper$0 = HEAP32[sp + 8 >> 2] | 0; + HEAP32[sp + 8 >> 2] = helper$0; HEAP32[sp + 24 >> 2] = 0; HEAP32[sp + 28 >> 2] = 0; - HEAP32[sp + 8 >> 2] = helper$0; chain$1(sp); helper$0 = HEAP32[sp + 8 >> 2] | 0; + HEAP32[sp + 8 >> 2] = helper$0; HEAP32[sp + 16 >> 2] = 0; HEAP32[sp + 20 >> 2] = 0; - HEAP32[sp + 8 >> 2] = helper$0; chain$0(sp); helper$0 = HEAP32[sp + 8 >> 2] | 0; STACKTOP = sp; @@ -472,8 +472,8 @@ function mix$1(sp) { function vars$0(sp) { sp = sp | 0; var x = 0, y = +0; - y = +HEAPF32[sp + 16 >> 2]; x = HEAP32[sp + 8 >> 2] | 0; + y = +HEAPF32[sp + 16 >> 2]; c(5 + (x + y)); c(6 + y * x); c(7 + (x + y)); @@ -482,8 +482,8 @@ function vars$0(sp) { function vars$1(sp) { sp = sp | 0; var x = 0, y = +0; - y = +HEAPF32[sp + 16 >> 2]; x = HEAP32[sp + 8 >> 2] | 0; + y = +HEAPF32[sp + 16 >> 2]; c(1 + (x + y)); c(2 + y * x); c(3 + (x + y)); @@ -492,8 +492,8 @@ function vars$1(sp) { function vars2$0(sp) { sp = sp | 0; var a = 0, b = +0; - b = +HEAPF32[sp + 32 >> 2]; a = HEAP32[sp + 24 >> 2] | 0; + b = +HEAPF32[sp + 32 >> 2]; a = c(3 + a); b = c(4 + b); a = c(5 + a); @@ -516,8 +516,8 @@ function vars3$0(sp) { function vars3$1(sp) { sp = sp | 0; var a = 0, x = 0, y = +0; - y = +HEAPF32[sp + 16 >> 2]; x = HEAP32[sp + 8 >> 2] | 0; + y = +HEAPF32[sp + 16 >> 2]; a = HEAP32[sp + 24 >> 2] | 0; a = x + y; a = c(1 + a); @@ -528,9 +528,9 @@ function vars3$1(sp) { function vars4$0(sp) { sp = sp | 0; var a = 0, x = 0, b = +0; - b = +HEAPF32[sp + 32 >> 2]; x = HEAP32[sp + 8 >> 2] | 0; a = HEAP32[sp + 24 >> 2] | 0; + b = +HEAPF32[sp + 32 >> 2]; a = c(4 + a); a = c(5 + a); a = c(6 + a); @@ -541,22 +541,22 @@ function vars4$0(sp) { function vars4$1(sp) { sp = sp | 0; var y = +0, x = 0, a = 0, b = +0; - b = +HEAPF32[sp + 32 >> 2]; - a = HEAP32[sp + 24 >> 2] | 0; x = HEAP32[sp + 8 >> 2] | 0; y = +HEAPF32[sp + 16 >> 2]; + a = HEAP32[sp + 24 >> 2] | 0; + b = +HEAPF32[sp + 32 >> 2]; b = y * x; a = c(1 + a); a = c(2 + a); a = c(3 + a); - HEAPF32[sp + 32 >> 2] = b; HEAP32[sp + 24 >> 2] = a; + HEAPF32[sp + 32 >> 2] = b; } function vars_w_stack$0(sp) { sp = sp | 0; var a = 0, b = +0; - b = +HEAPF32[sp + 48 >> 2]; a = HEAP32[sp + 40 >> 2] | 0; + b = +HEAPF32[sp + 48 >> 2]; a = c(4 + a); a = c(5 + a); a = c(6 + a); @@ -568,16 +568,16 @@ function vars_w_stack$0(sp) { function vars_w_stack$1(sp) { sp = sp | 0; var y = +0, x = 0, a = 0, b = +0; - b = +HEAPF32[sp + 48 >> 2]; - a = HEAP32[sp + 40 >> 2] | 0; x = HEAP32[sp + 24 >> 2] | 0; y = +HEAPF32[sp + 32 >> 2]; + a = HEAP32[sp + 40 >> 2] | 0; + b = +HEAPF32[sp + 48 >> 2]; b = y * x; a = c(1 + a); a = c(2 + a); a = c(3 + a); - HEAPF32[sp + 48 >> 2] = b; HEAP32[sp + 40 >> 2] = a; + HEAPF32[sp + 48 >> 2] = b; } function chain$0(sp) { sp = sp | 0; diff --git a/tools/test-js-optimizer-asm-outline2-output.js b/tools/test-js-optimizer-asm-outline2-output.js index 9f0be278fbd18..5acac67305478 100644 --- a/tools/test-js-optimizer-asm-outline2-output.js +++ b/tools/test-js-optimizer-asm-outline2-output.js @@ -50,19 +50,19 @@ function _free($mem) { if (($mem + (-8 - $21 | 0) | 0) >>> 0 < $5 >>> 0) { _abort(); } - HEAP32[sp + 664 >> 2] = 0; - HEAP32[sp + 668 >> 2] = 0; - HEAP32[sp + 48 >> 2] = $25; HEAP32[sp + 8 >> 2] = $mem; + HEAP32[sp + 16 >> 2] = $5; HEAP32[sp + 24 >> 2] = $10; - HEAP32[sp + 56 >> 2] = $26; HEAP32[sp + 40 >> 2] = $21; - HEAP32[sp + 16 >> 2] = $5; - HEAP32[sp + 224 >> 2] = $p_0; + HEAP32[sp + 48 >> 2] = $25; + HEAP32[sp + 56 >> 2] = $26; HEAP32[sp + 216 >> 2] = $psize_0; + HEAP32[sp + 224 >> 2] = $p_0; + HEAP32[sp + 664 >> 2] = 0; + HEAP32[sp + 668 >> 2] = 0; _free$1(sp); - $p_0 = HEAP32[sp + 224 >> 2] | 0; $psize_0 = HEAP32[sp + 216 >> 2] | 0; + $p_0 = HEAP32[sp + 224 >> 2] | 0; tempValue = HEAP32[sp + 664 >> 2] | 0; tempInt = HEAP32[sp + 668 >> 2] | 0; tempDouble = +HEAPF32[sp + 668 >> 2]; @@ -104,15 +104,15 @@ function _free($mem) { if (($194 & 1 | 0) == 0) { _abort(); } - HEAP32[sp + 672 >> 2] = 0; - HEAP32[sp + 676 >> 2] = 0; - HEAP32[sp + 240 >> 2] = $194; + HEAP32[sp + 8 >> 2] = $mem; + HEAP32[sp + 24 >> 2] = $10; HEAP32[sp + 32 >> 2] = $16; HEAP32[sp + 216 >> 2] = $psize_0; HEAP32[sp + 224 >> 2] = $p_0; HEAP32[sp + 232 >> 2] = $189; - HEAP32[sp + 8 >> 2] = $mem; - HEAP32[sp + 24 >> 2] = $10; + HEAP32[sp + 240 >> 2] = $194; + HEAP32[sp + 672 >> 2] = 0; + HEAP32[sp + 676 >> 2] = 0; _free$2(sp); tempValue = HEAP32[sp + 672 >> 2] | 0; tempInt = HEAP32[sp + 676 >> 2] | 0; @@ -158,14 +158,14 @@ function linear$1(sp) { function _free$0(sp) { sp = sp | 0; var $16 = 0, $220 = 0, $psize_0 = 0, $p_0 = 0, $189 = 0, $227 = 0, $194 = 0, $233 = 0, $mem = 0, $10 = 0, $236 = 0, $_pre_phi305 = 0, $267 = 0, $270 = 0, $273 = 0, $294 = 0, $299 = 0, $R7_1 = 0, $R7_0 = 0, $RP9_0 = 0, $301 = 0, $302 = 0, $305 = 0, $306 = 0, $278 = 0, $320 = 0, $351 = 0, $364 = 0, $psize_1 = 0; - $psize_1 = HEAP32[sp + 424 >> 2] | 0; - $10 = HEAP32[sp + 24 >> 2] | 0; $mem = HEAP32[sp + 8 >> 2] | 0; - $194 = HEAP32[sp + 240 >> 2] | 0; - $189 = HEAP32[sp + 232 >> 2] | 0; - $p_0 = HEAP32[sp + 224 >> 2] | 0; - $psize_0 = HEAP32[sp + 216 >> 2] | 0; + $10 = HEAP32[sp + 24 >> 2] | 0; $16 = HEAP32[sp + 32 >> 2] | 0; + $psize_0 = HEAP32[sp + 216 >> 2] | 0; + $p_0 = HEAP32[sp + 224 >> 2] | 0; + $189 = HEAP32[sp + 232 >> 2] | 0; + $194 = HEAP32[sp + 240 >> 2] | 0; + $psize_1 = HEAP32[sp + 424 >> 2] | 0; OL : do { if (($16 | 0) == (HEAP32[25] | 0)) { $220 = (HEAP32[22] | 0) + $psize_0 | 0; @@ -345,14 +345,14 @@ function _free$0(sp) { function _free$1(sp) { sp = sp | 0; var $25 = 0, $mem = 0, $10 = 0, $26 = 0, $21 = 0, $37 = 0, $40 = 0, $5 = 0, $_pre_phi307 = 0, $69 = 0, $72 = 0, $75 = 0, $95 = 0, $100 = 0, $R_1 = 0, $R_0 = 0, $RP_0 = 0, $102 = 0, $103 = 0, $106 = 0, $107 = 0, $80 = 0, $120 = 0, $151 = 0, $164 = 0, $p_0 = 0, $psize_0 = 0; - $psize_0 = HEAP32[sp + 216 >> 2] | 0; - $p_0 = HEAP32[sp + 224 >> 2] | 0; + $mem = HEAP32[sp + 8 >> 2] | 0; $5 = HEAP32[sp + 16 >> 2] | 0; - $21 = HEAP32[sp + 40 >> 2] | 0; - $26 = HEAP32[sp + 56 >> 2] | 0; $10 = HEAP32[sp + 24 >> 2] | 0; - $mem = HEAP32[sp + 8 >> 2] | 0; + $21 = HEAP32[sp + 40 >> 2] | 0; $25 = HEAP32[sp + 48 >> 2] | 0; + $26 = HEAP32[sp + 56 >> 2] | 0; + $psize_0 = HEAP32[sp + 216 >> 2] | 0; + $p_0 = HEAP32[sp + 224 >> 2] | 0; OL : do { if (($25 | 0) == (HEAP32[25] | 0)) { if ((HEAP32[($mem + (($10 & -8) - 4) | 0) >> 2] & 3 | 0) != 3) { @@ -542,18 +542,18 @@ function _free$1(sp) { break OL; } } while (0); - HEAP32[sp + 224 >> 2] = $p_0; HEAP32[sp + 216 >> 2] = $psize_0; + HEAP32[sp + 224 >> 2] = $p_0; } function _free$2(sp) { sp = sp | 0; - var helper$1 = 0, $194 = 0, $16 = 0, $204 = 0, $psize_0 = 0, $p_0 = 0, $189 = 0, $mem = 0, $10 = 0, $psize_1 = 0, $390 = 0, $396 = 0, $F16_0 = 0, $_pre_phi = 0, $404 = 0, $414 = 0, $415 = 0, $I18_0 = 0, $428 = 0, $436 = 0, $443 = 0, $447 = 0, $448 = 0, $463 = 0, $K19_0 = 0, $T_0 = 0, $472 = 0, $473 = 0, label = 0, $486 = 0, $487 = 0, $489 = 0, $501 = 0, $sp_0_in_i = 0, $sp_0_i = 0; - $10 = HEAP32[sp + 24 >> 2] | 0; + var helper$1 = 0, $194 = 0, $16 = 0, $204 = 0, $psize_0 = 0, $p_0 = 0, $mem = 0, $10 = 0, $189 = 0, $psize_1 = 0, $390 = 0, $396 = 0, $F16_0 = 0, $_pre_phi = 0, $404 = 0, $414 = 0, $415 = 0, $I18_0 = 0, $428 = 0, $436 = 0, $443 = 0, $447 = 0, $448 = 0, $463 = 0, $K19_0 = 0, $T_0 = 0, $472 = 0, $473 = 0, label = 0, $486 = 0, $487 = 0, $489 = 0, $501 = 0, $sp_0_in_i = 0, $sp_0_i = 0; $mem = HEAP32[sp + 8 >> 2] | 0; - $189 = HEAP32[sp + 232 >> 2] | 0; - $p_0 = HEAP32[sp + 224 >> 2] | 0; - $psize_0 = HEAP32[sp + 216 >> 2] | 0; + $10 = HEAP32[sp + 24 >> 2] | 0; $16 = HEAP32[sp + 32 >> 2] | 0; + $psize_0 = HEAP32[sp + 216 >> 2] | 0; + $p_0 = HEAP32[sp + 224 >> 2] | 0; + $189 = HEAP32[sp + 232 >> 2] | 0; $194 = HEAP32[sp + 240 >> 2] | 0; OL : do { do { @@ -578,16 +578,16 @@ function _free$2(sp) { HEAP32[sp + 672 >> 2] = 5; break OL; } - HEAP32[sp + 656 >> 2] = 0; - HEAP32[sp + 660 >> 2] = 0; + HEAP32[sp + 8 >> 2] = $mem; + HEAP32[sp + 24 >> 2] = $10; HEAP32[sp + 32 >> 2] = $16; HEAP32[sp + 216 >> 2] = $psize_0; HEAP32[sp + 224 >> 2] = $p_0; HEAP32[sp + 232 >> 2] = $189; HEAP32[sp + 240 >> 2] = $194; - HEAP32[sp + 8 >> 2] = $mem; - HEAP32[sp + 24 >> 2] = $10; HEAP32[sp + 424 >> 2] = $psize_1; + HEAP32[sp + 656 >> 2] = 0; + HEAP32[sp + 660 >> 2] = 0; _free$0(sp); $psize_1 = HEAP32[sp + 424 >> 2] | 0; tempValue = HEAP32[sp + 656 >> 2] | 0; From ae236b288c46dab914a137670e9366e0b0cc4c09 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 9 Aug 2013 16:48:05 -0700 Subject: [PATCH 020/112] warn in em++ if PYTHON is not defined properly --- em++ | 2 ++ 1 file changed, 2 insertions(+) diff --git a/em++ b/em++ index ba09e1a2d472d..810b7aec07a75 100755 --- a/em++ +++ b/em++ @@ -8,5 +8,7 @@ import os, subprocess, sys from tools import shared os.environ['EMMAKEN_CXX'] = '1' +if not os.path.exists(shared.PYTHON): + print >> sys.stderr, 'warning: PYTHON does not seem to be defined properly in ~/.emscripten (%s)' % shared.PYTHON exit(subprocess.call([shared.PYTHON, shared.EMCC] + sys.argv[1:])) From 3adfd036d7bbfbae1b1c1cf7de4fc9850ce06ad4 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 9 Aug 2013 17:19:33 -0700 Subject: [PATCH 021/112] aim for more evenly-sized pieces in outliner --- tests/runner.py | 10 +- tools/js-optimizer.js | 24 +- tools/shared.py | 2 +- .../test-js-optimizer-asm-outline1-output.js | 315 ++++------ .../test-js-optimizer-asm-outline2-output.js | 562 +++++++++--------- 5 files changed, 387 insertions(+), 526 deletions(-) diff --git a/tests/runner.py b/tests/runner.py index cbdacc2cdfaad..115559612b81b 100755 --- a/tests/runner.py +++ b/tests/runner.py @@ -11379,11 +11379,11 @@ def measure_funcs(filename): ([], { 100: (190, 250), 250: (200, 330), - 500: (250, 310), - 1000: (230, 300), - 2000: (380, 450), - 5000: (800, 1100), - 0: (1500, 1800) + 500: (250, 500), + 1000: (230, 1000), + 2000: (380, 2000), + 5000: (800, 5000), + 0: (1500, 5000) }), (['-O2'], { 100: (0, 1500), diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js index 5f1c6df86a9c5..ff6aee670a599 100644 --- a/tools/js-optimizer.js +++ b/tools/js-optimizer.js @@ -3106,7 +3106,9 @@ function outline(ast) { // Reserve an extra two spots per possible outlining: one for control flow var, the other for control flow data // The control variables are zeroed out when calling an outlined function, and after using // the value after they return. - asmData.maxOutlinings = Math.round(3*measureSize(func)/extraInfo.sizeToOutline); + var size = measureSize(func); + asmData.maxOutlinings = Math.round(3*size/extraInfo.sizeToOutline); + asmData.intendedPieces = Math.ceil(size/extraInfo.sizeToOutline); asmData.totalStackSize = stackSize + (stack.length + 2*asmData.maxOutlinings)*8; asmData.controlStackPos = function(i) { return stackSize + (stack.length + i)*8 }; asmData.controlDataStackPos = function(i) { return stackSize + (stack.length + i)*8 + 4 }; @@ -3211,13 +3213,15 @@ function outline(ast) { var CONTROL_BREAK = 1, CONTROL_BREAK_LABEL = 2, CONTROL_CONTINUE = 3, CONTROL_CONTINUE_LABEL = 4, CONTROL_RETURN_VOID = 5, CONTROL_RETURN_INT = 6, CONTROL_RETURN_DOUBLE = 7; var sizeToOutline = null; // customized per function and as we make progress - function calculateThreshold(func) { - sizeToOutline = extraInfo.sizeToOutline; + function calculateThreshold(func, asmData) { var size = measureSize(func); - //var desiredChunks = Math.ceil(size/extraInfo.sizeToOutline); - ////sizeToOutline = Math.round((extraInfo.sizeToOutline + (2*size/desiredChunks))/3); - //sizeToOutline = Math.round(size/desiredChunks); - printErr('trying to reduce the size of ' + func[1] + ' which is ' + size + ' (>= ' + extraInfo.sizeToOutline + '), aim for ' + sizeToOutline); + if (size <= extraInfo.sizeToOutline) { + sizeToOutline = Infinity; + printErr(' no point in trying to reduce the size of ' + func[1] + ' which is ' + size + ' <= ' + extraInfo.sizeToOutline); + } else { + sizeToOutline = Math.round(size/Math.max(2, asmData.intendedPieces--)); + printErr('trying to reduce the size of ' + func[1] + ' which is ' + size + ' (>=? ' + extraInfo.sizeToOutline + '), aim for ' + sizeToOutline); + } } var level = 0, loops = 0; @@ -3446,7 +3450,7 @@ function outline(ast) { } outliningParents[newIdent] = func[1]; printErr('performed outline ' + [func[1], newIdent, 'code sizes (pre/post):', originalCodeSize, measureSize(code), 'overhead (w/r):', setSize(setSub(codeInfo.writes, owned)), setSize(setSub(codeInfo.reads, owned)), ' owned: ', setSize(owned), ' left: ', setSize(asmData.vars), setSize(asmData.params), ' loopsDepth: ', loops]); - calculateThreshold(func); + calculateThreshold(func, asmData); return [newFunc]; } @@ -3526,6 +3530,7 @@ function outline(ast) { if (ret.length > pre) { // we outlined recursively, reset our state here //printErr('successful outline in recursion ' + func[1] + ' due to recursive in level ' + level); + if (measureSize(func) <= extraInfo.sizeToOutline) break; end = i-1; sizeSeen = 0; canRestart = true; @@ -3565,6 +3570,7 @@ function outline(ast) { if (newFuncs.length) { ret.push.apply(ret, newFuncs); } + if (measureSize(func) <= extraInfo.sizeToOutline) break; sizeSeen = 0; end = i-1; canRestart = true; @@ -3596,8 +3602,8 @@ function outline(ast) { if (size >= extraInfo.sizeToOutline) { aggressiveVariableElimination(func, asmData); flatten(func, asmData); - calculateThreshold(func); analyzeFunction(func, asmData); + calculateThreshold(func, asmData); var stats = getStatements(func); var ret = outlineStatements(func, asmData, stats, 0.9*size); assert(level == 0); diff --git a/tools/shared.py b/tools/shared.py index 37d568e77a254..08231af9b45ad 100644 --- a/tools/shared.py +++ b/tools/shared.py @@ -630,7 +630,7 @@ def line_splitter(data): return out -def limit_size(string, MAX=120*20): +def limit_size(string, MAX=12000*20): if len(string) < MAX: return string return string[0:MAX/2] + '\n[..]\n' + string[-MAX/2:] diff --git a/tools/test-js-optimizer-asm-outline1-output.js b/tools/test-js-optimizer-asm-outline1-output.js index de0747f9225ee..904e56cf0c3c3 100644 --- a/tools/test-js-optimizer-asm-outline1-output.js +++ b/tools/test-js-optimizer-asm-outline1-output.js @@ -5,7 +5,6 @@ function lin() { c(1); c(2); c(3); - c(4); HEAP32[sp + 16 >> 2] = 0; HEAP32[sp + 20 >> 2] = 0; lin$1(sp); @@ -21,8 +20,6 @@ function lin2() { while (1) { c(1); c(2); - c(3); - c(4); HEAP32[sp + 16 >> 2] = 0; HEAP32[sp + 20 >> 2] = 0; lin2$1(sp); @@ -42,9 +39,14 @@ function lin3() { c(3); c(4); c(5); - HEAP32[sp + 16 >> 2] = 0; - HEAP32[sp + 20 >> 2] = 0; - lin3$1(sp); + c(6); + c(7); + c(8); + c(9); + c(10); + c(11); + c(12); + c(13); HEAP32[sp + 8 >> 2] = 0; HEAP32[sp + 12 >> 2] = 0; lin3$0(sp); @@ -70,9 +72,15 @@ function lin4() { c(2); c(3); c(4); - HEAP32[sp + 16 >> 2] = 0; - HEAP32[sp + 20 >> 2] = 0; - lin4$1(sp); + c(5); + c(6); + c(7); + c(8); + c(9); + c(10); + c(11); + c(12); + c(13); HEAP32[sp + 8 >> 2] = 0; HEAP32[sp + 12 >> 2] = 0; lin4$0(sp); @@ -97,9 +105,15 @@ function lin5() { c(2); c(3); c(4); - HEAP32[sp + 16 >> 2] = 0; - HEAP32[sp + 20 >> 2] = 0; - lin5$1(sp); + c(5); + c(6); + c(7); + c(8); + c(9); + c(10); + c(11); + c(12); + c(13); HEAP32[sp + 8 >> 2] = 0; HEAP32[sp + 12 >> 2] = 0; lin5$0(sp); @@ -120,13 +134,6 @@ function mix() { sp = STACKTOP; STACKTOP = STACKTOP + 168 | 0; main : while (1) { - c(1); - c(2); - c(3); - c(4); - c(5); - c(6); - c(7); HEAP32[sp + 16 >> 2] = 0; HEAP32[sp + 20 >> 2] = 0; mix$1(sp); @@ -170,11 +177,10 @@ function vars(x, y) { var sp = 0; sp = STACKTOP; STACKTOP = STACKTOP + 152 | 0; - HEAP32[sp + 8 >> 2] = x; - HEAPF32[sp + 16 >> 2] = y; - HEAP32[sp + 32 >> 2] = 0; - HEAP32[sp + 36 >> 2] = 0; - vars$1(sp); + c(1 + (x + y)); + c(2 + y * x); + c(3 + (x + y)); + c(4 + y * x); HEAP32[sp + 8 >> 2] = x; HEAPF32[sp + 16 >> 2] = y; HEAP32[sp + 24 >> 2] = 0; @@ -192,6 +198,7 @@ function vars2(x, y) { b = y * x; a = c(1 + a); b = c(2 + b); + a = c(3 + a); HEAP32[sp + 24 >> 2] = a; HEAPF32[sp + 32 >> 2] = b; HEAP32[sp + 40 >> 2] = 0; @@ -207,13 +214,11 @@ function vars3(x, y) { var a = 0, sp = 0; sp = STACKTOP; STACKTOP = STACKTOP + 160 | 0; - HEAP32[sp + 8 >> 2] = x; - HEAPF32[sp + 16 >> 2] = y; - HEAP32[sp + 24 >> 2] = a; - HEAP32[sp + 40 >> 2] = 0; - HEAP32[sp + 44 >> 2] = 0; - vars3$1(sp); - a = HEAP32[sp + 24 >> 2] | 0; + a = x + y; + a = c(1 + a); + a = c(2 + y * x); + a = c(3 + a); + a = c(4 + y * x); HEAP32[sp + 8 >> 2] = x; HEAPF32[sp + 16 >> 2] = y; HEAP32[sp + 24 >> 2] = a; @@ -230,15 +235,11 @@ function vars4(x, y) { sp = STACKTOP; STACKTOP = STACKTOP + 168 | 0; a = x + y; - HEAP32[sp + 8 >> 2] = x; - HEAPF32[sp + 16 >> 2] = y; - HEAP32[sp + 24 >> 2] = a; - HEAPF32[sp + 32 >> 2] = b; - HEAP32[sp + 48 >> 2] = 0; - HEAP32[sp + 52 >> 2] = 0; - vars4$1(sp); - a = HEAP32[sp + 24 >> 2] | 0; - b = +HEAPF32[sp + 32 >> 2]; + b = y * x; + a = c(1 + a); + a = c(2 + a); + a = c(3 + a); + a = c(4 + a); HEAP32[sp + 8 >> 2] = x; HEAP32[sp + 24 >> 2] = a; HEAPF32[sp + 32 >> 2] = b; @@ -255,7 +256,6 @@ function vars_w_stack(x, y) { var a = 0, b = +0, sp = 0; sp = STACKTOP; STACKTOP = STACKTOP + 208 | 0; - a = x + y; HEAP32[sp + 24 >> 2] = x; HEAPF32[sp + 32 >> 2] = y; HEAP32[sp + 40 >> 2] = a; @@ -276,19 +276,9 @@ function vars_w_stack(x, y) { function chain() { var helper$0 = 0, sp = 0; sp = STACKTOP; - STACKTOP = STACKTOP + 336 | 0; + STACKTOP = STACKTOP + 240 | 0; helper$0 = 1; HEAP32[sp + 8 >> 2] = helper$0; - HEAP32[sp + 56 >> 2] = 0; - HEAP32[sp + 60 >> 2] = 0; - chain$5(sp); - helper$0 = HEAP32[sp + 8 >> 2] | 0; - HEAP32[sp + 8 >> 2] = helper$0; - HEAP32[sp + 48 >> 2] = 0; - HEAP32[sp + 52 >> 2] = 0; - chain$4(sp); - helper$0 = HEAP32[sp + 8 >> 2] | 0; - HEAP32[sp + 8 >> 2] = helper$0; HEAP32[sp + 40 >> 2] = 0; HEAP32[sp + 44 >> 2] = 0; chain$3(sp); @@ -298,21 +288,10 @@ function chain() { HEAP32[sp + 36 >> 2] = 0; chain$2(sp); helper$0 = HEAP32[sp + 8 >> 2] | 0; - HEAP32[sp + 8 >> 2] = helper$0; - HEAP32[sp + 24 >> 2] = 0; - HEAP32[sp + 28 >> 2] = 0; - chain$1(sp); - helper$0 = HEAP32[sp + 8 >> 2] | 0; - HEAP32[sp + 8 >> 2] = helper$0; - HEAP32[sp + 16 >> 2] = 0; - HEAP32[sp + 20 >> 2] = 0; - chain$0(sp); - helper$0 = HEAP32[sp + 8 >> 2] | 0; STACKTOP = sp; } function lin$0(sp) { sp = sp | 0; - c(13); c(14); c(15); c(16); @@ -323,6 +302,7 @@ function lin$0(sp) { } function lin$1(sp) { sp = sp | 0; + c(4); c(5); c(6); c(7); @@ -331,10 +311,10 @@ function lin$1(sp) { c(10); c(11); c(12); + c(13); } function lin2$0(sp) { sp = sp | 0; - c(13); c(14); c(15); c(16); @@ -345,6 +325,8 @@ function lin2$0(sp) { } function lin2$1(sp) { sp = sp | 0; + c(3); + c(4); c(5); c(6); c(7); @@ -353,6 +335,7 @@ function lin2$1(sp) { c(10); c(11); c(12); + c(13); } function lin3$0(sp) { sp = sp | 0; @@ -369,21 +352,9 @@ function lin3$0(sp) { break OL; } while (0); } -function lin3$1(sp) { - sp = sp | 0; - c(6); - c(7); - c(8); - c(9); - c(10); - c(11); - c(12); - c(13); -} function lin4$0(sp) { sp = sp | 0; OL : do { - c(13); c(14); c(15); c(16); @@ -395,21 +366,9 @@ function lin4$0(sp) { break OL; } while (0); } -function lin4$1(sp) { - sp = sp | 0; - c(5); - c(6); - c(7); - c(8); - c(9); - c(10); - c(11); - c(12); -} function lin5$0(sp) { sp = sp | 0; OL : do { - c(13); c(14); c(15); c(16); @@ -421,22 +380,9 @@ function lin5$0(sp) { break OL; } while (0); } -function lin5$1(sp) { - sp = sp | 0; - c(5); - c(6); - c(7); - c(8); - c(9); - c(10); - c(11); - c(12); -} function mix$0(sp) { sp = sp | 0; OL : do { - c(16); - c(17); HEAP32[sp + 8 >> 2] = 2; HEAP32[sp + 12 >> 2] = 2; break OL; @@ -460,6 +406,13 @@ function mix$0(sp) { } function mix$1(sp) { sp = sp | 0; + c(1); + c(2); + c(3); + c(4); + c(5); + c(6); + c(7); c(8); c(9); c(10); @@ -468,6 +421,8 @@ function mix$1(sp) { c(13); c(14); c(15); + c(16); + c(17); } function vars$0(sp) { sp = sp | 0; @@ -479,22 +434,11 @@ function vars$0(sp) { c(7 + (x + y)); c(8 + y * x); } -function vars$1(sp) { - sp = sp | 0; - var x = 0, y = +0; - x = HEAP32[sp + 8 >> 2] | 0; - y = +HEAPF32[sp + 16 >> 2]; - c(1 + (x + y)); - c(2 + y * x); - c(3 + (x + y)); - c(4 + y * x); -} function vars2$0(sp) { sp = sp | 0; - var a = 0, b = +0; + var b = +0, a = 0; a = HEAP32[sp + 24 >> 2] | 0; b = +HEAPF32[sp + 32 >> 2]; - a = c(3 + a); b = c(4 + b); a = c(5 + a); b = c(6 + b); @@ -507,57 +451,28 @@ function vars3$0(sp) { x = HEAP32[sp + 8 >> 2] | 0; y = +HEAPF32[sp + 16 >> 2]; a = HEAP32[sp + 24 >> 2] | 0; - a = c(4 + y * x); a = c(5 + a); a = c(6 + y * x); a = c(7 + a); HEAP32[sp + 24 >> 2] = a; } -function vars3$1(sp) { - sp = sp | 0; - var a = 0, x = 0, y = +0; - x = HEAP32[sp + 8 >> 2] | 0; - y = +HEAPF32[sp + 16 >> 2]; - a = HEAP32[sp + 24 >> 2] | 0; - a = x + y; - a = c(1 + a); - a = c(2 + y * x); - a = c(3 + a); - HEAP32[sp + 24 >> 2] = a; -} function vars4$0(sp) { sp = sp | 0; var a = 0, x = 0, b = +0; x = HEAP32[sp + 8 >> 2] | 0; a = HEAP32[sp + 24 >> 2] | 0; b = +HEAPF32[sp + 32 >> 2]; - a = c(4 + a); a = c(5 + a); a = c(6 + a); b = c(7 + a + x); HEAP32[sp + 24 >> 2] = a; HEAPF32[sp + 32 >> 2] = b; } -function vars4$1(sp) { - sp = sp | 0; - var y = +0, x = 0, a = 0, b = +0; - x = HEAP32[sp + 8 >> 2] | 0; - y = +HEAPF32[sp + 16 >> 2]; - a = HEAP32[sp + 24 >> 2] | 0; - b = +HEAPF32[sp + 32 >> 2]; - b = y * x; - a = c(1 + a); - a = c(2 + a); - a = c(3 + a); - HEAP32[sp + 24 >> 2] = a; - HEAPF32[sp + 32 >> 2] = b; -} function vars_w_stack$0(sp) { sp = sp | 0; var a = 0, b = +0; a = HEAP32[sp + 40 >> 2] | 0; b = +HEAPF32[sp + 48 >> 2]; - a = c(4 + a); a = c(5 + a); a = c(6 + a); b = c(7 + a); @@ -567,15 +482,17 @@ function vars_w_stack$0(sp) { } function vars_w_stack$1(sp) { sp = sp | 0; - var y = +0, x = 0, a = 0, b = +0; + var a = 0, x = 0, y = +0, b = +0; x = HEAP32[sp + 24 >> 2] | 0; y = +HEAPF32[sp + 32 >> 2]; a = HEAP32[sp + 40 >> 2] | 0; b = +HEAPF32[sp + 48 >> 2]; + a = x + y; b = y * x; a = c(1 + a); a = c(2 + a); a = c(3 + a); + a = c(4 + a); HEAP32[sp + 40 >> 2] = a; HEAPF32[sp + 48 >> 2] = b; } @@ -583,23 +500,22 @@ function chain$0(sp) { sp = sp | 0; var helper$0 = 0; helper$0 = HEAP32[sp + 8 >> 2] | 0; - if (helper$0) { - helper$0 = 0; - if (x == 11) { - print(11); - } else if (x == 12) { - print(12); - } else { - helper$0 = 1; - } - } - if (helper$0) { - helper$0 = 0; - if (1) { - print(99); - } else { - helper$0 = 1; - } + if (x == 7) { + print(7); + } else if (x == 8) { + print(8); + } else if (x == 9) { + print(9); + } else if (x == 10) { + print(10); + } else if (x == 11) { + print(11); + } else if (x == 12) { + print(12); + } else if (1) { + print(99); + } else { + helper$0 = 1; } HEAP32[sp + 8 >> 2] = helper$0; } @@ -607,15 +523,20 @@ function chain$1(sp) { sp = sp | 0; var helper$0 = 0; helper$0 = HEAP32[sp + 8 >> 2] | 0; - if (helper$0) { - helper$0 = 0; - if (x == 9) { - print(9); - } else if (x == 10) { - print(10); - } else { - helper$0 = 1; - } + if (x == 1) { + print(1); + } else if (x == 2) { + print(2); + } else if (x == 3) { + print(3); + } else if (x == 4) { + print(4); + } else if (x == 5) { + print(5); + } else if (x == 6) { + print(6); + } else { + helper$0 = 1; } HEAP32[sp + 8 >> 2] = helper$0; } @@ -625,13 +546,11 @@ function chain$2(sp) { helper$0 = HEAP32[sp + 8 >> 2] | 0; if (helper$0) { helper$0 = 0; - if (x == 7) { - print(7); - } else if (x == 8) { - print(8); - } else { - helper$0 = 1; - } + HEAP32[sp + 8 >> 2] = helper$0; + HEAP32[sp + 16 >> 2] = 0; + HEAP32[sp + 20 >> 2] = 0; + chain$0(sp); + helper$0 = HEAP32[sp + 8 >> 2] | 0; } HEAP32[sp + 8 >> 2] = helper$0; } @@ -641,45 +560,11 @@ function chain$3(sp) { helper$0 = HEAP32[sp + 8 >> 2] | 0; if (helper$0) { helper$0 = 0; - if (x == 5) { - print(5); - } else if (x == 6) { - print(6); - } else { - helper$0 = 1; - } - } - HEAP32[sp + 8 >> 2] = helper$0; -} -function chain$4(sp) { - sp = sp | 0; - var helper$0 = 0; - helper$0 = HEAP32[sp + 8 >> 2] | 0; - if (helper$0) { - helper$0 = 0; - if (x == 3) { - print(3); - } else if (x == 4) { - print(4); - } else { - helper$0 = 1; - } - } - HEAP32[sp + 8 >> 2] = helper$0; -} -function chain$5(sp) { - sp = sp | 0; - var helper$0 = 0; - helper$0 = HEAP32[sp + 8 >> 2] | 0; - if (helper$0) { - helper$0 = 0; - if (x == 1) { - print(1); - } else if (x == 2) { - print(2); - } else { - helper$0 = 1; - } + HEAP32[sp + 8 >> 2] = helper$0; + HEAP32[sp + 24 >> 2] = 0; + HEAP32[sp + 28 >> 2] = 0; + chain$1(sp); + helper$0 = HEAP32[sp + 8 >> 2] | 0; } HEAP32[sp + 8 >> 2] = helper$0; } diff --git a/tools/test-js-optimizer-asm-outline2-output.js b/tools/test-js-optimizer-asm-outline2-output.js index 5acac67305478..28feb6cb26316 100644 --- a/tools/test-js-optimizer-asm-outline2-output.js +++ b/tools/test-js-optimizer-asm-outline2-output.js @@ -11,6 +11,8 @@ function linear() { cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0); cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0); cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0); + cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0); + cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0); HEAP32[sp + 16 >> 2] = 0; HEAP32[sp + 20 >> 2] = 0; linear$1(sp); @@ -21,7 +23,7 @@ function linear() { } function _free($mem) { $mem = $mem | 0; - var $5 = 0, $10 = 0, $16 = 0, $21 = 0, $25 = 0, $26 = 0, $psize_0 = 0, $p_0 = 0, $189 = 0, $194 = 0, sp = 0, helper$0 = 0; + var $5 = 0, $10 = 0, $16 = 0, $21 = 0, $25 = 0, $26 = 0, $psize_0 = 0, $p_0 = 0, $189 = 0, $194 = 0, sp = 0; sp = STACKTOP; if (($mem | 0) == 0) { STACKTOP = sp; @@ -37,63 +39,59 @@ function _free($mem) { } $16 = $mem + (($10 & -8) - 8) | 0; L621 : do { - helper$0 = 1; - if (helper$0) { - helper$0 = 0; - if (($10 & 1 | 0) == 0) { - $21 = HEAP32[($mem - 8 | 0) >> 2] | 0; - if (($10 & 3 | 0) == 0) { - return; - } - $25 = $mem + (-8 - $21 | 0) | 0; - $26 = $21 + ($10 & -8) | 0; - if (($mem + (-8 - $21 | 0) | 0) >>> 0 < $5 >>> 0) { - _abort(); - } - HEAP32[sp + 8 >> 2] = $mem; - HEAP32[sp + 16 >> 2] = $5; - HEAP32[sp + 24 >> 2] = $10; - HEAP32[sp + 40 >> 2] = $21; - HEAP32[sp + 48 >> 2] = $25; - HEAP32[sp + 56 >> 2] = $26; - HEAP32[sp + 216 >> 2] = $psize_0; - HEAP32[sp + 224 >> 2] = $p_0; - HEAP32[sp + 664 >> 2] = 0; - HEAP32[sp + 668 >> 2] = 0; - _free$1(sp); - $psize_0 = HEAP32[sp + 216 >> 2] | 0; - $p_0 = HEAP32[sp + 224 >> 2] | 0; - tempValue = HEAP32[sp + 664 >> 2] | 0; - tempInt = HEAP32[sp + 668 >> 2] | 0; - tempDouble = +HEAPF32[sp + 668 >> 2]; - HEAP32[sp + 664 >> 2] = 0; - HEAP32[sp + 668 >> 2] = 0; - if ((tempValue | 0) == 5) { - return; - } - if ((tempValue | 0) == 1) { + if (($10 & 1 | 0) == 0) { + $21 = HEAP32[($mem - 8 | 0) >> 2] | 0; + if (($10 & 3 | 0) == 0) { + return; + } + $25 = $mem + (-8 - $21 | 0) | 0; + $26 = $21 + ($10 & -8) | 0; + if (($mem + (-8 - $21 | 0) | 0) >>> 0 < $5 >>> 0) { + _abort(); + } + if (($25 | 0) == (HEAP32[25] | 0)) { + if ((HEAP32[($mem + (($10 & -8) - 4) | 0) >> 2] & 3 | 0) != 3) { + $p_0 = $25; + $psize_0 = $26; break; } - if ((tempValue | 0) == 2) { - switch (tempInt | 0) { - case 2: - { - break L621; - } + HEAP32[22] = $26; + HEAP32[($mem + (($10 & -8) - 4) | 0) >> 2] = HEAP32[($mem + (($10 & -8) - 4) | 0) >> 2] & -2; + HEAP32[$mem + ((-8 - $21 | 0) + 4) >> 2] = $26 | 1; + HEAP32[($mem + (($10 & -8) - 8) | 0) >> 2] = $26; + return; + } + HEAP32[sp + 8 >> 2] = $mem; + HEAP32[sp + 16 >> 2] = $5; + HEAP32[sp + 40 >> 2] = $21; + HEAP32[sp + 48 >> 2] = $25; + HEAP32[sp + 56 >> 2] = $26; + HEAP32[sp + 216 >> 2] = $psize_0; + HEAP32[sp + 224 >> 2] = $p_0; + HEAP32[sp + 648 >> 2] = 0; + HEAP32[sp + 652 >> 2] = 0; + _free$1(sp); + $psize_0 = HEAP32[sp + 216 >> 2] | 0; + $p_0 = HEAP32[sp + 224 >> 2] | 0; + tempValue = HEAP32[sp + 648 >> 2] | 0; + tempInt = HEAP32[sp + 652 >> 2] | 0; + tempDouble = +HEAPF32[sp + 652 >> 2]; + HEAP32[sp + 648 >> 2] = 0; + HEAP32[sp + 652 >> 2] = 0; + if ((tempValue | 0) == 1) { + break; + } + if ((tempValue | 0) == 2) { + switch (tempInt | 0) { + case 2: + { + break L621; } } - } else { - helper$0 = 1; - } - } - if (helper$0) { - helper$0 = 0; - if (1) { - $p_0 = $mem - 8 | 0; - $psize_0 = $10 & -8; - } else { - helper$0 = 1; } + } else { + $p_0 = $mem - 8 | 0; + $psize_0 = $10 & -8; } } while (0); $189 = $p_0; @@ -111,14 +109,14 @@ function _free($mem) { HEAP32[sp + 224 >> 2] = $p_0; HEAP32[sp + 232 >> 2] = $189; HEAP32[sp + 240 >> 2] = $194; - HEAP32[sp + 672 >> 2] = 0; - HEAP32[sp + 676 >> 2] = 0; + HEAP32[sp + 656 >> 2] = 0; + HEAP32[sp + 660 >> 2] = 0; _free$2(sp); - tempValue = HEAP32[sp + 672 >> 2] | 0; - tempInt = HEAP32[sp + 676 >> 2] | 0; - tempDouble = +HEAPF32[sp + 676 >> 2]; - HEAP32[sp + 672 >> 2] = 0; - HEAP32[sp + 676 >> 2] = 0; + tempValue = HEAP32[sp + 656 >> 2] | 0; + tempInt = HEAP32[sp + 660 >> 2] | 0; + tempDouble = +HEAPF32[sp + 660 >> 2]; + HEAP32[sp + 656 >> 2] = 0; + HEAP32[sp + 660 >> 2] = 0; if ((tempValue | 0) == 5) { return; } @@ -137,7 +135,6 @@ function linear$0(sp) { cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0); cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0); cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0); - cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0); } function linear$1(sp) { sp = sp | 0; @@ -153,221 +150,186 @@ function linear$1(sp) { cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0); cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0); cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0); - cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0); } function _free$0(sp) { sp = sp | 0; - var $16 = 0, $220 = 0, $psize_0 = 0, $p_0 = 0, $189 = 0, $227 = 0, $194 = 0, $233 = 0, $mem = 0, $10 = 0, $236 = 0, $_pre_phi305 = 0, $267 = 0, $270 = 0, $273 = 0, $294 = 0, $299 = 0, $R7_1 = 0, $R7_0 = 0, $RP9_0 = 0, $301 = 0, $302 = 0, $305 = 0, $306 = 0, $278 = 0, $320 = 0, $351 = 0, $364 = 0, $psize_1 = 0; + var $194 = 0, $233 = 0, $mem = 0, $10 = 0, $236 = 0, $16 = 0, $_pre_phi305 = 0, $267 = 0, $270 = 0, $273 = 0, $294 = 0, $299 = 0, $R7_1 = 0, $R7_0 = 0, $RP9_0 = 0, $301 = 0, $302 = 0, $305 = 0, $306 = 0, $278 = 0, $320 = 0, $351 = 0, $364 = 0; $mem = HEAP32[sp + 8 >> 2] | 0; $10 = HEAP32[sp + 24 >> 2] | 0; $16 = HEAP32[sp + 32 >> 2] | 0; - $psize_0 = HEAP32[sp + 216 >> 2] | 0; - $p_0 = HEAP32[sp + 224 >> 2] | 0; - $189 = HEAP32[sp + 232 >> 2] | 0; $194 = HEAP32[sp + 240 >> 2] | 0; - $psize_1 = HEAP32[sp + 424 >> 2] | 0; OL : do { - if (($16 | 0) == (HEAP32[25] | 0)) { - $220 = (HEAP32[22] | 0) + $psize_0 | 0; - HEAP32[22] = $220; - HEAP32[25] = $p_0; - HEAP32[$p_0 + 4 >> 2] = $220 | 1; - HEAP32[$189 + $220 >> 2] = $220; - HEAP32[sp + 656 >> 2] = 5; - break OL; - } - $227 = ($194 & -8) + $psize_0 | 0; - L726 : do { - if ($194 >>> 0 < 256) { - $233 = HEAP32[$mem + ($10 & -8) >> 2] | 0; - $236 = HEAP32[$mem + ($10 & -8 | 4) >> 2] | 0; - do { - if (($233 | 0) != (120 + ($194 >>> 3 << 1 << 2) | 0 | 0)) { - if ($233 >>> 0 < (HEAP32[24] | 0) >>> 0) { - _abort(); - } - if ((HEAP32[$233 + 12 >> 2] | 0) == ($16 | 0)) { - break; - } + if ($194 >>> 0 < 256) { + $233 = HEAP32[$mem + ($10 & -8) >> 2] | 0; + $236 = HEAP32[$mem + ($10 & -8 | 4) >> 2] | 0; + do { + if (($233 | 0) != (120 + ($194 >>> 3 << 1 << 2) | 0 | 0)) { + if ($233 >>> 0 < (HEAP32[24] | 0) >>> 0) { _abort(); } - } while (0); - if (($236 | 0) == ($233 | 0)) { - HEAP32[20] = HEAP32[20] & (1 << ($194 >>> 3) ^ -1); - break; + if ((HEAP32[$233 + 12 >> 2] | 0) == ($16 | 0)) { + break; + } + _abort(); } - do { - if (($236 | 0) == (120 + ($194 >>> 3 << 1 << 2) | 0 | 0)) { - $_pre_phi305 = $236 + 8 | 0; - } else { - if ($236 >>> 0 < (HEAP32[24] | 0) >>> 0) { - _abort(); - } - if ((HEAP32[($236 + 8 | 0) >> 2] | 0) == ($16 | 0)) { - $_pre_phi305 = $236 + 8 | 0; - break; - } + } while (0); + if (($236 | 0) == ($233 | 0)) { + HEAP32[20] = HEAP32[20] & (1 << ($194 >>> 3) ^ -1); + HEAP32[sp + 640 >> 2] = 1; + break OL; + } + do { + if (($236 | 0) == (120 + ($194 >>> 3 << 1 << 2) | 0 | 0)) { + $_pre_phi305 = $236 + 8 | 0; + } else { + if ($236 >>> 0 < (HEAP32[24] | 0) >>> 0) { _abort(); } - } while (0); - HEAP32[$233 + 12 >> 2] = $236; - HEAP32[$_pre_phi305 >> 2] = $233; - } else { - $267 = $mem + (($10 & -8) - 8) | 0; - $270 = HEAP32[$mem + (($10 & -8) + 16) >> 2] | 0; - $273 = HEAP32[$mem + ($10 & -8 | 4) >> 2] | 0; - do { - if (($273 | 0) == ($267 | 0)) { - $294 = HEAP32[($mem + (($10 & -8) + 12) | 0) >> 2] | 0; - if (($294 | 0) == 0) { - $299 = HEAP32[($mem + (($10 & -8) + 8) | 0) >> 2] | 0; - if (($299 | 0) == 0) { - $R7_1 = 0; - break; - } else { - $R7_0 = $299; - $RP9_0 = $mem + (($10 & -8) + 8) | 0; - } - } else { - $R7_0 = $294; - $RP9_0 = $mem + (($10 & -8) + 12) | 0; - } - while (1) { - $301 = $R7_0 + 20 | 0; - $302 = HEAP32[$301 >> 2] | 0; - if (($302 | 0) != 0) { - $R7_0 = $302; - $RP9_0 = $301; - continue; - } - $305 = $R7_0 + 16 | 0; - $306 = HEAP32[$305 >> 2] | 0; - if (($306 | 0) == 0) { - break; - } else { - $R7_0 = $306; - $RP9_0 = $305; - } - } - if ($RP9_0 >>> 0 < (HEAP32[24] | 0) >>> 0) { - _abort(); - } else { - HEAP32[$RP9_0 >> 2] = 0; - $R7_1 = $R7_0; + if ((HEAP32[($236 + 8 | 0) >> 2] | 0) == ($16 | 0)) { + $_pre_phi305 = $236 + 8 | 0; + break; + } + _abort(); + } + } while (0); + HEAP32[$233 + 12 >> 2] = $236; + HEAP32[$_pre_phi305 >> 2] = $233; + } else { + $267 = $mem + (($10 & -8) - 8) | 0; + $270 = HEAP32[$mem + (($10 & -8) + 16) >> 2] | 0; + $273 = HEAP32[$mem + ($10 & -8 | 4) >> 2] | 0; + do { + if (($273 | 0) == ($267 | 0)) { + $294 = HEAP32[($mem + (($10 & -8) + 12) | 0) >> 2] | 0; + if (($294 | 0) == 0) { + $299 = HEAP32[($mem + (($10 & -8) + 8) | 0) >> 2] | 0; + if (($299 | 0) == 0) { + $R7_1 = 0; break; + } else { + $R7_0 = $299; + $RP9_0 = $mem + (($10 & -8) + 8) | 0; } } else { - $278 = HEAP32[$mem + ($10 & -8) >> 2] | 0; - if ($278 >>> 0 < (HEAP32[24] | 0) >>> 0) { - _abort(); - } - if ((HEAP32[($278 + 12 | 0) >> 2] | 0) != ($267 | 0)) { - _abort(); + $R7_0 = $294; + $RP9_0 = $mem + (($10 & -8) + 12) | 0; + } + while (1) { + $301 = $R7_0 + 20 | 0; + $302 = HEAP32[$301 >> 2] | 0; + if (($302 | 0) != 0) { + $R7_0 = $302; + $RP9_0 = $301; + continue; } - if ((HEAP32[($273 + 8 | 0) >> 2] | 0) == ($267 | 0)) { - HEAP32[($278 + 12 | 0) >> 2] = $273; - HEAP32[($273 + 8 | 0) >> 2] = $278; - $R7_1 = $273; + $305 = $R7_0 + 16 | 0; + $306 = HEAP32[$305 >> 2] | 0; + if (($306 | 0) == 0) { break; } else { - _abort(); + $R7_0 = $306; + $RP9_0 = $305; } } - } while (0); - if (($270 | 0) == 0) { - break; - } - $320 = 384 + (HEAP32[($mem + (($10 & -8) + 20) | 0) >> 2] << 2) | 0; - do { - if (($267 | 0) == (HEAP32[$320 >> 2] | 0)) { - HEAP32[$320 >> 2] = $R7_1; - if (($R7_1 | 0) != 0) { - break; - } - HEAP32[21] = HEAP32[21] & (1 << HEAP32[($mem + (($10 & -8) + 20) | 0) >> 2] ^ -1); - break L726; + if ($RP9_0 >>> 0 < (HEAP32[24] | 0) >>> 0) { + _abort(); } else { - if ($270 >>> 0 < (HEAP32[24] | 0) >>> 0) { - _abort(); - } - if ((HEAP32[($270 + 16 | 0) >> 2] | 0) == ($267 | 0)) { - HEAP32[($270 + 16 | 0) >> 2] = $R7_1; - } else { - HEAP32[$270 + 20 >> 2] = $R7_1; - } - if (($R7_1 | 0) == 0) { - break L726; - } + HEAP32[$RP9_0 >> 2] = 0; + $R7_1 = $R7_0; + break; } - } while (0); - if ($R7_1 >>> 0 < (HEAP32[24] | 0) >>> 0) { - _abort(); - } - HEAP32[$R7_1 + 24 >> 2] = $270; - $351 = HEAP32[$mem + (($10 & -8) + 8) >> 2] | 0; - do { - if (($351 | 0) != 0) { - if ($351 >>> 0 < (HEAP32[24] | 0) >>> 0) { - _abort(); - } else { - HEAP32[$R7_1 + 16 >> 2] = $351; - HEAP32[$351 + 24 >> 2] = $R7_1; - break; - } + } else { + $278 = HEAP32[$mem + ($10 & -8) >> 2] | 0; + if ($278 >>> 0 < (HEAP32[24] | 0) >>> 0) { + _abort(); + } + if ((HEAP32[($278 + 12 | 0) >> 2] | 0) != ($267 | 0)) { + _abort(); + } + if ((HEAP32[($273 + 8 | 0) >> 2] | 0) == ($267 | 0)) { + HEAP32[($278 + 12 | 0) >> 2] = $273; + HEAP32[($273 + 8 | 0) >> 2] = $278; + $R7_1 = $273; + break; + } else { + _abort(); } - } while (0); - $364 = HEAP32[$mem + (($10 & -8) + 12) >> 2] | 0; - if (($364 | 0) == 0) { - break; } - if ($364 >>> 0 < (HEAP32[24] | 0) >>> 0) { - _abort(); + } while (0); + if (($270 | 0) == 0) { + HEAP32[sp + 640 >> 2] = 1; + break OL; + } + $320 = 384 + (HEAP32[($mem + (($10 & -8) + 20) | 0) >> 2] << 2) | 0; + do { + if (($267 | 0) == (HEAP32[$320 >> 2] | 0)) { + HEAP32[$320 >> 2] = $R7_1; + if (($R7_1 | 0) != 0) { + break; + } + HEAP32[21] = HEAP32[21] & (1 << HEAP32[($mem + (($10 & -8) + 20) | 0) >> 2] ^ -1); + HEAP32[sp + 640 >> 2] = 2; + HEAP32[sp + 644 >> 2] = 2; + break OL; } else { - HEAP32[$R7_1 + 20 >> 2] = $364; - HEAP32[$364 + 24 >> 2] = $R7_1; - break; + if ($270 >>> 0 < (HEAP32[24] | 0) >>> 0) { + _abort(); + } + if ((HEAP32[($270 + 16 | 0) >> 2] | 0) == ($267 | 0)) { + HEAP32[($270 + 16 | 0) >> 2] = $R7_1; + } else { + HEAP32[$270 + 20 >> 2] = $R7_1; + } + if (($R7_1 | 0) == 0) { + HEAP32[sp + 640 >> 2] = 2; + HEAP32[sp + 644 >> 2] = 2; + break OL; + } } + } while (0); + if ($R7_1 >>> 0 < (HEAP32[24] | 0) >>> 0) { + _abort(); + } + HEAP32[$R7_1 + 24 >> 2] = $270; + $351 = HEAP32[$mem + (($10 & -8) + 8) >> 2] | 0; + do { + if (($351 | 0) != 0) { + if ($351 >>> 0 < (HEAP32[24] | 0) >>> 0) { + _abort(); + } else { + HEAP32[$R7_1 + 16 >> 2] = $351; + HEAP32[$351 + 24 >> 2] = $R7_1; + break; + } + } + } while (0); + $364 = HEAP32[$mem + (($10 & -8) + 12) >> 2] | 0; + if (($364 | 0) == 0) { + HEAP32[sp + 640 >> 2] = 1; + break OL; + } + if ($364 >>> 0 < (HEAP32[24] | 0) >>> 0) { + _abort(); + } else { + HEAP32[$R7_1 + 20 >> 2] = $364; + HEAP32[$364 + 24 >> 2] = $R7_1; + HEAP32[sp + 640 >> 2] = 1; + break OL; } - } while (0); - HEAP32[$p_0 + 4 >> 2] = $227 | 1; - HEAP32[$189 + $227 >> 2] = $227; - if (($p_0 | 0) != (HEAP32[25] | 0)) { - $psize_1 = $227; - HEAP32[sp + 656 >> 2] = 1; - break OL; } - HEAP32[22] = $227; - HEAP32[sp + 656 >> 2] = 5; - break OL; } while (0); - HEAP32[sp + 424 >> 2] = $psize_1; } function _free$1(sp) { sp = sp | 0; - var $25 = 0, $mem = 0, $10 = 0, $26 = 0, $21 = 0, $37 = 0, $40 = 0, $5 = 0, $_pre_phi307 = 0, $69 = 0, $72 = 0, $75 = 0, $95 = 0, $100 = 0, $R_1 = 0, $R_0 = 0, $RP_0 = 0, $102 = 0, $103 = 0, $106 = 0, $107 = 0, $80 = 0, $120 = 0, $151 = 0, $164 = 0, $p_0 = 0, $psize_0 = 0; + var $21 = 0, $37 = 0, $mem = 0, $40 = 0, $5 = 0, $25 = 0, $26 = 0, $_pre_phi307 = 0, $69 = 0, $72 = 0, $75 = 0, $95 = 0, $100 = 0, $R_1 = 0, $R_0 = 0, $RP_0 = 0, $102 = 0, $103 = 0, $106 = 0, $107 = 0, $80 = 0, $120 = 0, $151 = 0, $164 = 0, $p_0 = 0, $psize_0 = 0; $mem = HEAP32[sp + 8 >> 2] | 0; $5 = HEAP32[sp + 16 >> 2] | 0; - $10 = HEAP32[sp + 24 >> 2] | 0; $21 = HEAP32[sp + 40 >> 2] | 0; $25 = HEAP32[sp + 48 >> 2] | 0; $26 = HEAP32[sp + 56 >> 2] | 0; $psize_0 = HEAP32[sp + 216 >> 2] | 0; $p_0 = HEAP32[sp + 224 >> 2] | 0; OL : do { - if (($25 | 0) == (HEAP32[25] | 0)) { - if ((HEAP32[($mem + (($10 & -8) - 4) | 0) >> 2] & 3 | 0) != 3) { - $p_0 = $25; - $psize_0 = $26; - HEAP32[sp + 664 >> 2] = 1; - break OL; - } - HEAP32[22] = $26; - HEAP32[($mem + (($10 & -8) - 4) | 0) >> 2] = HEAP32[($mem + (($10 & -8) - 4) | 0) >> 2] & -2; - HEAP32[$mem + ((-8 - $21 | 0) + 4) >> 2] = $26 | 1; - HEAP32[($mem + (($10 & -8) - 8) | 0) >> 2] = $26; - HEAP32[sp + 664 >> 2] = 5; - break OL; - } if ($21 >>> 0 < 256) { $37 = HEAP32[$mem + ((-8 - $21 | 0) + 8) >> 2] | 0; $40 = HEAP32[$mem + ((-8 - $21 | 0) + 12) >> 2] | 0; @@ -386,7 +348,7 @@ function _free$1(sp) { HEAP32[20] = HEAP32[20] & (1 << ($21 >>> 3) ^ -1); $p_0 = $25; $psize_0 = $26; - HEAP32[sp + 664 >> 2] = 1; + HEAP32[sp + 648 >> 2] = 1; break OL; } do { @@ -407,7 +369,7 @@ function _free$1(sp) { HEAP32[$_pre_phi307 >> 2] = $37; $p_0 = $25; $psize_0 = $26; - HEAP32[sp + 664 >> 2] = 1; + HEAP32[sp + 648 >> 2] = 1; break OL; } $69 = $mem + (-8 - $21 | 0) | 0; @@ -474,7 +436,7 @@ function _free$1(sp) { if (($72 | 0) == 0) { $p_0 = $25; $psize_0 = $26; - HEAP32[sp + 664 >> 2] = 1; + HEAP32[sp + 648 >> 2] = 1; break OL; } $120 = 384 + (HEAP32[($mem + ((-8 - $21 | 0) + 28) | 0) >> 2] << 2) | 0; @@ -487,8 +449,8 @@ function _free$1(sp) { HEAP32[21] = HEAP32[21] & (1 << HEAP32[($mem + ((-8 - $21 | 0) + 28) | 0) >> 2] ^ -1); $p_0 = $25; $psize_0 = $26; - HEAP32[sp + 664 >> 2] = 2; - HEAP32[sp + 668 >> 2] = 2; + HEAP32[sp + 648 >> 2] = 2; + HEAP32[sp + 652 >> 2] = 2; break OL; } else { if ($72 >>> 0 < (HEAP32[24] | 0) >>> 0) { @@ -502,8 +464,8 @@ function _free$1(sp) { if (($R_1 | 0) == 0) { $p_0 = $25; $psize_0 = $26; - HEAP32[sp + 664 >> 2] = 2; - HEAP32[sp + 668 >> 2] = 2; + HEAP32[sp + 648 >> 2] = 2; + HEAP32[sp + 652 >> 2] = 2; break OL; } } @@ -528,7 +490,7 @@ function _free$1(sp) { if (($164 | 0) == 0) { $p_0 = $25; $psize_0 = $26; - HEAP32[sp + 664 >> 2] = 1; + HEAP32[sp + 648 >> 2] = 1; break OL; } if ($164 >>> 0 < (HEAP32[24] | 0) >>> 0) { @@ -538,7 +500,7 @@ function _free$1(sp) { HEAP32[$164 + 24 >> 2] = $R_1; $p_0 = $25; $psize_0 = $26; - HEAP32[sp + 664 >> 2] = 1; + HEAP32[sp + 648 >> 2] = 1; break OL; } } while (0); @@ -547,7 +509,7 @@ function _free$1(sp) { } function _free$2(sp) { sp = sp | 0; - var helper$1 = 0, $194 = 0, $16 = 0, $204 = 0, $psize_0 = 0, $p_0 = 0, $mem = 0, $10 = 0, $189 = 0, $psize_1 = 0, $390 = 0, $396 = 0, $F16_0 = 0, $_pre_phi = 0, $404 = 0, $414 = 0, $415 = 0, $I18_0 = 0, $428 = 0, $436 = 0, $443 = 0, $447 = 0, $448 = 0, $463 = 0, $K19_0 = 0, $T_0 = 0, $472 = 0, $473 = 0, label = 0, $486 = 0, $487 = 0, $489 = 0, $501 = 0, $sp_0_in_i = 0, $sp_0_i = 0; + var $194 = 0, $16 = 0, $204 = 0, $psize_0 = 0, $p_0 = 0, $220 = 0, $189 = 0, $227 = 0, $mem = 0, $10 = 0, $psize_1 = 0, $390 = 0, $396 = 0, $F16_0 = 0, $_pre_phi = 0, $404 = 0, $414 = 0, $415 = 0, $I18_0 = 0, $428 = 0, $436 = 0, $443 = 0, $447 = 0, $448 = 0, $463 = 0, $K19_0 = 0, $T_0 = 0, $472 = 0, $473 = 0, label = 0, $486 = 0, $487 = 0, $489 = 0, $501 = 0, $sp_0_in_i = 0, $sp_0_i = 0; $mem = HEAP32[sp + 8 >> 2] | 0; $10 = HEAP32[sp + 24 >> 2] | 0; $16 = HEAP32[sp + 32 >> 2] | 0; @@ -557,65 +519,73 @@ function _free$2(sp) { $194 = HEAP32[sp + 240 >> 2] | 0; OL : do { do { - helper$1 = 1; - if (helper$1) { - helper$1 = 0; - if (($194 & 2 | 0) == 0) { - if (($16 | 0) == (HEAP32[26] | 0)) { - $204 = (HEAP32[23] | 0) + $psize_0 | 0; - HEAP32[23] = $204; - HEAP32[26] = $p_0; - HEAP32[$p_0 + 4 >> 2] = $204 | 1; - if (($p_0 | 0) == (HEAP32[25] | 0)) { - HEAP32[25] = 0; - HEAP32[22] = 0; - } - if ($204 >>> 0 <= (HEAP32[27] | 0) >>> 0) { - HEAP32[sp + 672 >> 2] = 5; - break OL; - } - _sys_trim(0) | 0; - HEAP32[sp + 672 >> 2] = 5; + if (($194 & 2 | 0) == 0) { + if (($16 | 0) == (HEAP32[26] | 0)) { + $204 = (HEAP32[23] | 0) + $psize_0 | 0; + HEAP32[23] = $204; + HEAP32[26] = $p_0; + HEAP32[$p_0 + 4 >> 2] = $204 | 1; + if (($p_0 | 0) == (HEAP32[25] | 0)) { + HEAP32[25] = 0; + HEAP32[22] = 0; + } + if ($204 >>> 0 <= (HEAP32[27] | 0) >>> 0) { + HEAP32[sp + 656 >> 2] = 5; break OL; } + _sys_trim(0) | 0; + HEAP32[sp + 656 >> 2] = 5; + break OL; + } + if (($16 | 0) == (HEAP32[25] | 0)) { + $220 = (HEAP32[22] | 0) + $psize_0 | 0; + HEAP32[22] = $220; + HEAP32[25] = $p_0; + HEAP32[$p_0 + 4 >> 2] = $220 | 1; + HEAP32[$189 + $220 >> 2] = $220; + HEAP32[sp + 656 >> 2] = 5; + break OL; + } + $227 = ($194 & -8) + $psize_0 | 0; + L726 : do { HEAP32[sp + 8 >> 2] = $mem; HEAP32[sp + 24 >> 2] = $10; HEAP32[sp + 32 >> 2] = $16; - HEAP32[sp + 216 >> 2] = $psize_0; - HEAP32[sp + 224 >> 2] = $p_0; - HEAP32[sp + 232 >> 2] = $189; HEAP32[sp + 240 >> 2] = $194; - HEAP32[sp + 424 >> 2] = $psize_1; - HEAP32[sp + 656 >> 2] = 0; - HEAP32[sp + 660 >> 2] = 0; + HEAP32[sp + 640 >> 2] = 0; + HEAP32[sp + 644 >> 2] = 0; _free$0(sp); - $psize_1 = HEAP32[sp + 424 >> 2] | 0; - tempValue = HEAP32[sp + 656 >> 2] | 0; - tempInt = HEAP32[sp + 660 >> 2] | 0; - tempDouble = +HEAPF32[sp + 660 >> 2]; - HEAP32[sp + 656 >> 2] = 0; - HEAP32[sp + 660 >> 2] = 0; - if ((tempValue | 0) == 5) { - HEAP32[sp + 672 >> 2] = 5; - break OL; - } + tempValue = HEAP32[sp + 640 >> 2] | 0; + tempInt = HEAP32[sp + 644 >> 2] | 0; + tempDouble = +HEAPF32[sp + 644 >> 2]; + HEAP32[sp + 640 >> 2] = 0; + HEAP32[sp + 644 >> 2] = 0; if ((tempValue | 0) == 1) { break; } - } else { - helper$1 = 1; - } - } - if (helper$1) { - helper$1 = 0; - if (1) { - HEAP32[($mem + (($10 & -8) - 4) | 0) >> 2] = $194 & -2; - HEAP32[$p_0 + 4 >> 2] = $psize_0 | 1; - HEAP32[$189 + $psize_0 >> 2] = $psize_0; - $psize_1 = $psize_0; - } else { - helper$1 = 1; + if ((tempValue | 0) == 2) { + switch (tempInt | 0) { + case 2: + { + break L726; + } + } + } + } while (0); + HEAP32[$p_0 + 4 >> 2] = $227 | 1; + HEAP32[$189 + $227 >> 2] = $227; + if (($p_0 | 0) != (HEAP32[25] | 0)) { + $psize_1 = $227; + break; } + HEAP32[22] = $227; + HEAP32[sp + 656 >> 2] = 5; + break OL; + } else { + HEAP32[($mem + (($10 & -8) - 4) | 0) >> 2] = $194 & -2; + HEAP32[$p_0 + 4 >> 2] = $psize_0 | 1; + HEAP32[$189 + $psize_0 >> 2] = $psize_0; + $psize_1 = $psize_0; } } while (0); $390 = $psize_1 >>> 3; @@ -640,7 +610,7 @@ function _free$2(sp) { HEAP32[$F16_0 + 12 >> 2] = $p_0; HEAP32[$p_0 + 8 >> 2] = $F16_0; HEAP32[$p_0 + 12 >> 2] = 120 + ($390 << 1 << 2) | 0; - HEAP32[sp + 672 >> 2] = 5; + HEAP32[sp + 656 >> 2] = 5; break OL; } $414 = $p_0; @@ -727,7 +697,7 @@ function _free$2(sp) { if (($501 | 0) == 0) { $sp_0_in_i = 536; } else { - HEAP32[sp + 672 >> 2] = 5; + HEAP32[sp + 656 >> 2] = 5; break OL; } while (1) { @@ -740,7 +710,7 @@ function _free$2(sp) { } HEAP32[28] = -1; STACKTOP = sp; - HEAP32[sp + 672 >> 2] = 5; + HEAP32[sp + 656 >> 2] = 5; break OL; } while (0); } From 0a39d6902565771e6a07a8b1e0229d257dfc0b7c Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 9 Aug 2013 17:21:25 -0700 Subject: [PATCH 022/112] fix test_exit_stack --- tests/runner.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/runner.py b/tests/runner.py index 115559612b81b..d6ec37a32e17c 100755 --- a/tests/runner.py +++ b/tests/runner.py @@ -3254,7 +3254,7 @@ def test_exit_stack(self): ''') self.emcc_args += ['--pre-js', 'pre.js'] - self.do_run(src, '''reported\nExit Status: 1\npostRun\nok.\n''') + self.do_run(src, '''reported\nexit(1) called\nExit Status: 1\npostRun\nok.\n''') def test_class(self): src = ''' From c2b064eb49ea63eec59add5751c8a9c2b80dab92 Mon Sep 17 00:00:00 2001 From: ToadKing Date: Fri, 9 Aug 2013 20:44:38 -0400 Subject: [PATCH 023/112] use performance timers in openal --- src/library_openal.js | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/library_openal.js b/src/library_openal.js index c55415b8cb610..627b2dc91ca33 100644 --- a/src/library_openal.js +++ b/src/library_openal.js @@ -56,6 +56,12 @@ var LibraryOpenAL = { return 0; } }, + + getCurrentTime: function() { + // currentTime is frozen during execution, use perfomrance timers to + // emulate the current time at call time + return (window['performance']['now']() - AL.currentContext.startTime) / 1000.0; + }, alcCreateContext__deps: ['updateSources'], alcCreateContext: function(device, attrList) { @@ -85,7 +91,8 @@ var LibraryOpenAL = { err: 0, src: [], buf: [], - interval: setInterval(function() { _updateSources(context); }, AL.QUEUE_INTERVAL) + interval: setInterval(function() { _updateSources(context); }, AL.QUEUE_INTERVAL), + startTime: window['performance']['now']() }; AL.contexts.push(context); return AL.contexts.length; @@ -101,7 +108,7 @@ var LibraryOpenAL = { } }, - updateSource__deps: ['setSourceState'], + updateSource__deps: ['setSourceState', 'getCurrentTime'], updateSource: function(src) { #if OPENAL_DEBUG var idx = AL.currentContext.src.indexOf(src); @@ -110,7 +117,7 @@ var LibraryOpenAL = { return; } - var currentTime = AL.currentContext.ctx.currentTime; + var currentTime = _getCurrentTime(); var startTime = src.bufferPosition; for (var i = src.buffersPlayed; i < src.queue.length; i++) { @@ -153,7 +160,7 @@ var LibraryOpenAL = { } }, - setSourceState__deps: ['updateSource', 'stopSourceQueue'], + setSourceState__deps: ['updateSource', 'stopSourceQueue', 'getCurrentTime'], setSourceState: function(src, state) { #if OPENAL_DEBUG var idx = AL.currentContext.src.indexOf(src); @@ -162,7 +169,7 @@ var LibraryOpenAL = { if (src.state !== 0x1013 /* AL_PAUSED */) { src.state = 0x1012 /* AL_PLAYING */; // Reset our position. - src.bufferPosition = AL.currentContext.ctx.currentTime; + src.bufferPosition = _getCurrentTime(); src.buffersPlayed = 0; #if OPENAL_DEBUG console.log('setSourceState resetting and playing source ' + idx); @@ -170,7 +177,7 @@ var LibraryOpenAL = { } else { src.state = 0x1012 /* AL_PLAYING */; // Use the current offset from src.bufferPosition to resume at the correct point. - src.bufferPosition = AL.currentContext.ctx.currentTime - src.bufferPosition; + src.bufferPosition = _getCurrentTime() - src.bufferPosition; #if OPENAL_DEBUG console.log('setSourceState resuming source ' + idx + ' at ' + src.bufferPosition.toFixed(4)); #endif @@ -181,7 +188,7 @@ var LibraryOpenAL = { if (src.state === 0x1012 /* AL_PLAYING */) { src.state = 0x1013 /* AL_PAUSED */; // Store off the current offset to restore with on resume. - src.bufferPosition = AL.currentContext.ctx.currentTime - src.bufferPosition; + src.bufferPosition = _getCurrentTime() - src.bufferPosition; _stopSourceQueue(src); #if OPENAL_DEBUG console.log('setSourceState pausing source ' + idx + ' at ' + src.bufferPosition.toFixed(4)); From 5c2647401735205df744bbf2e7c95f3d772fd1df Mon Sep 17 00:00:00 2001 From: Anthony Pesch Date: Fri, 9 Aug 2013 18:23:23 -0700 Subject: [PATCH 024/112] - moved enet tests and relay script - added support for SOCKK define to override ports when compiling --- tests/runner.py | 6 ++--- tests/{ => sockets}/socket_relay.py | 0 .../test_enet_client.c} | 24 ++++++++++++------- .../test_enet_server.c} | 15 +++++++++--- 4 files changed, 30 insertions(+), 15 deletions(-) rename tests/{ => sockets}/socket_relay.py (100%) rename tests/{enet_client.c => sockets/test_enet_client.c} (95%) rename tests/{enet_server.c => sockets/test_enet_server.c} (96%) diff --git a/tests/runner.py b/tests/runner.py index bd98fe6370343..ddee682ee8b43 100755 --- a/tests/runner.py +++ b/tests/runner.py @@ -14107,7 +14107,7 @@ def partial(q): def make_relay_server(self, port1, port2): def relay_server(q): print >> sys.stderr, 'creating relay server on ports %d,%d' % (port1, port2) - proc = Popen([PYTHON, path_from_root('tests', 'socket_relay.py'), str(port1), str(port2)]) + proc = Popen([PYTHON, path_from_root('tests', 'sockets/socket_relay.py'), str(port1), str(port2)]) q.put(proc.pid) proc.communicate() return relay_server @@ -14214,12 +14214,12 @@ def test_enet(self): Popen([PYTHON, path_from_root('emmake'), 'make']).communicate() enet = [self.in_dir('enet', '.libs', 'libenet.a'), '-I'+path_from_root('tests', 'enet', 'include')] os.chdir(pwd) - Popen([PYTHON, EMCC, path_from_root('tests', 'enet_server.c'), '-o', 'server.html'] + enet).communicate() + Popen([PYTHON, EMCC, path_from_root('tests', 'sockets/test_enet_server.c'), '-o', 'server.html', '-DSOCKK=1235'] + enet).communicate() try: with self.WebsockHarness(1234, self.make_relay_server(1234, 1236)): with self.WebsockHarness(1236, no_server=True): - self.btest('enet_client.c', expected='0', args=enet) + self.btest('sockets/test_enet_client.c', expected='0', args=['-DSOCKK=1237'] + enet) finally: self.clean_pids() diff --git a/tests/socket_relay.py b/tests/sockets/socket_relay.py similarity index 100% rename from tests/socket_relay.py rename to tests/sockets/socket_relay.py diff --git a/tests/enet_client.c b/tests/sockets/test_enet_client.c similarity index 95% rename from tests/enet_client.c rename to tests/sockets/test_enet_client.c index 601b8769e0824..bf14375cd13b8 100644 --- a/tests/enet_client.c +++ b/tests/sockets/test_enet_client.c @@ -1,20 +1,22 @@ #include -#include - +#include #include +#if EMSCRIPTEN +#include +#endif ENetHost * host; void main_loop() { - static int counter = 0; #if EMSCRIPTEN + static int counter = 0; counter++; -#endif if (counter == 100) { printf("stop!\n"); emscripten_cancel_main_loop(); return; } +#endif ENetEvent event; if (enet_host_service (host, & event, 0) == 0) return; @@ -32,7 +34,11 @@ void main_loop() { event.channelID); int result = strcmp("packetfoo", event.packet->data); +#if EMSCRIPTEN REPORT_RESULT(); +#else + exit(EXIT_SUCCESS); +#endif /* Clean up the packet now that we're done using it. */ enet_packet_destroy (event.packet); @@ -73,11 +79,7 @@ int main (int argc, char ** argv) ENetAddress address; enet_address_set_host (& address, "localhost"); -#if EMSCRIPTEN - address.port = 1237; -#else - address.port = 1235; -#endif + address.port = SOCKK; printf("connecting to server...\n"); @@ -100,7 +102,11 @@ int main (int argc, char ** argv) "console.log('added.');"); #endif +#if EMSCRIPTEN emscripten_set_main_loop(main_loop, 3, 1); +#else + while (1) main_loop(); +#endif return 1; } diff --git a/tests/enet_server.c b/tests/sockets/test_enet_server.c similarity index 96% rename from tests/enet_server.c rename to tests/sockets/test_enet_server.c index a8167e162ca9f..9a4518ac82884 100644 --- a/tests/enet_server.c +++ b/tests/sockets/test_enet_server.c @@ -1,10 +1,13 @@ // g++ -fpermissive ../enet_server.c -I/home/alon/Dev/emscripten/system/include/emscripten/ -Iinclude/ -fpermissive .libs/libenet.a -o enet_server ; g++ ../enet_client.c -I/home/alon/Dev/emscripten/system/include/emscripten/ -Iinclude/ -fpermissive .libs/libenet.a -o enet_client #include -#include - +#include #include +#if EMSCRIPTEN +#include +#endif + ENetHost *host; void send_msg(ENetPeer *peer) { @@ -31,7 +34,9 @@ void main_loop() { #endif if (counter == 100) { printf("stop!\n"); +#if EMSCRIPTEN emscripten_cancel_main_loop(); +#endif return; } @@ -82,7 +87,7 @@ int main (int argc, char ** argv) ENetAddress address; address.host = ENET_HOST_ANY; - address.port = 1235; + address.port = SOCKK; printf("create!\n"); host = enet_host_create (& address /* the address to bind the server host to */, 32 /* allow up to 32 clients and/or outgoing connections */, @@ -96,7 +101,11 @@ int main (int argc, char ** argv) exit (EXIT_FAILURE); } +#if EMSCRIPTEN emscripten_set_main_loop(main_loop, 3, 1); +#else + while (1) main_loop(); +#endif return 1; } From 3bef3b2cd64d3e6061eef6de08a6d384e59f7c27 Mon Sep 17 00:00:00 2001 From: Bruce Mitchener Date: Sat, 10 Aug 2013 09:38:28 +0700 Subject: [PATCH 025/112] Remove errno codes that don't exist in musl. --- src/library.js | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/library.js b/src/library.js index f0302aaa8f56f..14fd3e1bb1447 100644 --- a/src/library.js +++ b/src/library.js @@ -8217,10 +8217,8 @@ LibraryManager.library = { ECOMM: {{{ cDefine('ECOMM') }}}, EPROTO: {{{ cDefine('EPROTO') }}}, EMULTIHOP: {{{ cDefine('EMULTIHOP') }}}, - ELBIN: {{{ cDefine('ELBIN') }}}, EDOTDOT: {{{ cDefine('EDOTDOT') }}}, EBADMSG: {{{ cDefine('EBADMSG') }}}, - EFTYPE: {{{ cDefine('EFTYPE') }}}, ENOTUNIQ: {{{ cDefine('ENOTUNIQ') }}}, EBADFD: {{{ cDefine('EBADFD') }}}, EREMCHG: {{{ cDefine('EREMCHG') }}}, @@ -8230,7 +8228,6 @@ LibraryManager.library = { ELIBMAX: {{{ cDefine('ELIBMAX') }}}, ELIBEXEC: {{{ cDefine('ELIBEXEC') }}}, ENOSYS: {{{ cDefine('ENOSYS') }}}, - ENMFILE: {{{ cDefine('ENMFILE') }}}, ENOTEMPTY: {{{ cDefine('ENOTEMPTY') }}}, ENAMETOOLONG: {{{ cDefine('ENAMETOOLONG') }}}, ELOOP: {{{ cDefine('ELOOP') }}}, @@ -8262,14 +8259,11 @@ LibraryManager.library = { EISCONN: {{{ cDefine('EISCONN') }}}, ENOTCONN: {{{ cDefine('ENOTCONN') }}}, ETOOMANYREFS: {{{ cDefine('ETOOMANYREFS') }}}, - EPROCLIM: {{{ cDefine('EPROCLIM') }}}, EUSERS: {{{ cDefine('EUSERS') }}}, EDQUOT: {{{ cDefine('EDQUOT') }}}, ESTALE: {{{ cDefine('ESTALE') }}}, ENOTSUP: {{{ cDefine('ENOTSUP') }}}, ENOMEDIUM: {{{ cDefine('ENOMEDIUM') }}}, - ENOSHARE: {{{ cDefine('ENOSHARE') }}}, - ECASECLASH: {{{ cDefine('ECASECLASH') }}}, EILSEQ: {{{ cDefine('EILSEQ') }}}, EOVERFLOW: {{{ cDefine('EOVERFLOW') }}}, ECANCELED: {{{ cDefine('ECANCELED') }}}, @@ -8346,10 +8340,8 @@ LibraryManager.library = { {{{ cDefine('ECOMM') }}}: 'Communication error on send', {{{ cDefine('EPROTO') }}}: 'Protocol error', {{{ cDefine('EMULTIHOP') }}}: 'Multihop attempted', - {{{ cDefine('ELBIN') }}}: 'Inode is remote (not really error)', {{{ cDefine('EDOTDOT') }}}: 'Cross mount point (not really error)', {{{ cDefine('EBADMSG') }}}: 'Trying to read unreadable message', - {{{ cDefine('EFTYPE') }}}: 'Inappropriate file type or format', {{{ cDefine('ENOTUNIQ') }}}: 'Given log. name not unique', {{{ cDefine('EBADFD') }}}: 'f.d. invalid for this operation', {{{ cDefine('EREMCHG') }}}: 'Remote address changed', @@ -8359,7 +8351,6 @@ LibraryManager.library = { {{{ cDefine('ELIBMAX') }}}: 'Attempting to link in too many libs', {{{ cDefine('ELIBEXEC') }}}: 'Attempting to exec a shared library', {{{ cDefine('ENOSYS') }}}: 'Function not implemented', - {{{ cDefine('ENMFILE') }}}: 'No more files', {{{ cDefine('ENOTEMPTY') }}}: 'Directory not empty', {{{ cDefine('ENAMETOOLONG') }}}: 'File or path name too long', {{{ cDefine('ELOOP') }}}: 'Too many symbolic links', @@ -8391,14 +8382,11 @@ LibraryManager.library = { {{{ cDefine('EISCONN') }}}: 'Socket is already connected', {{{ cDefine('ENOTCONN') }}}: 'Socket is not connected', {{{ cDefine('ETOOMANYREFS') }}}: 'TOOMANYREFS', - {{{ cDefine('EPROCLIM') }}}: 'EPROCLIM', {{{ cDefine('EUSERS') }}}: 'EUSERS', {{{ cDefine('EDQUOT') }}}: 'EDQUOT', {{{ cDefine('ESTALE') }}}: 'ESTALE', {{{ cDefine('ENOTSUP') }}}: 'Not supported', {{{ cDefine('ENOMEDIUM') }}}: 'No medium (in tape drive)', - {{{ cDefine('ENOSHARE') }}}: 'No such host or network path', - {{{ cDefine('ECASECLASH') }}}: 'Filename exists with different case', {{{ cDefine('EILSEQ') }}}: 'EILSEQ', {{{ cDefine('EOVERFLOW') }}}: 'Value too large for defined data type', {{{ cDefine('ECANCELED') }}}: 'Operation canceled', From 69c93a14f3c6d0596624bf02679386ee6bf20949 Mon Sep 17 00:00:00 2001 From: Bruce Mitchener Date: Sat, 10 Aug 2013 09:51:07 +0700 Subject: [PATCH 026/112] Provide additional errno messages. --- src/library.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/library.js b/src/library.js index 14fd3e1bb1447..6ce7726880b15 100644 --- a/src/library.js +++ b/src/library.js @@ -8378,16 +8378,16 @@ LibraryManager.library = { {{{ cDefine('EPROTONOSUPPORT') }}}: 'Unknown protocol', {{{ cDefine('ESOCKTNOSUPPORT') }}}: 'Socket type not supported', {{{ cDefine('EADDRNOTAVAIL') }}}: 'Address not available', - {{{ cDefine('ENETRESET') }}}: 'ENETRESET', + {{{ cDefine('ENETRESET') }}}: 'Connection reset by network', {{{ cDefine('EISCONN') }}}: 'Socket is already connected', {{{ cDefine('ENOTCONN') }}}: 'Socket is not connected', - {{{ cDefine('ETOOMANYREFS') }}}: 'TOOMANYREFS', - {{{ cDefine('EUSERS') }}}: 'EUSERS', - {{{ cDefine('EDQUOT') }}}: 'EDQUOT', - {{{ cDefine('ESTALE') }}}: 'ESTALE', + {{{ cDefine('ETOOMANYREFS') }}}: 'Too many references', + {{{ cDefine('EUSERS') }}}: 'Too many users', + {{{ cDefine('EDQUOT') }}}: 'Quota exceeded', + {{{ cDefine('ESTALE') }}}: 'Stale file handle', {{{ cDefine('ENOTSUP') }}}: 'Not supported', {{{ cDefine('ENOMEDIUM') }}}: 'No medium (in tape drive)', - {{{ cDefine('EILSEQ') }}}: 'EILSEQ', + {{{ cDefine('EILSEQ') }}}: 'Illegal byte sequence', {{{ cDefine('EOVERFLOW') }}}: 'Value too large for defined data type', {{{ cDefine('ECANCELED') }}}: 'Operation canceled', {{{ cDefine('ENOTRECOVERABLE') }}}: 'State not recoverable', From bfd1151621a2edddbcc118bc05c8399fda1235d8 Mon Sep 17 00:00:00 2001 From: Bruce Mitchener Date: Sat, 10 Aug 2013 09:59:23 +0700 Subject: [PATCH 027/112] Remove some errno settings. This is a follow up to the previous commit that removed them from src/library.js. --- src/settings.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/settings.js b/src/settings.js index 21745ef83ac59..3515091d6e165 100644 --- a/src/settings.js +++ b/src/settings.js @@ -1391,10 +1391,8 @@ var C_DEFINES = { 'ECOMM': '70', 'EPROTO': '71', 'EMULTIHOP': '74', - 'ELBIN': '75', 'EDOTDOT': '76', 'EBADMSG': '77', - 'EFTYPE': '79', 'ENOTUNIQ': '80', 'EBADFD': '81', 'EREMCHG': '82', @@ -1404,7 +1402,6 @@ var C_DEFINES = { 'ELIBMAX': '86', 'ELIBEXEC': '87', 'ENOSYS': '88', - 'ENMFILE': '89', 'ENOTEMPTY': '90', 'ENAMETOOLONG': '91', 'ELOOP': '92', @@ -1436,14 +1433,11 @@ var C_DEFINES = { 'EISCONN': '127', 'ENOTCONN': '128', 'ETOOMANYREFS': '129', - 'EPROCLIM': '130', 'EUSERS': '131', 'EDQUOT': '132', 'ESTALE': '133', 'ENOTSUP': '134', 'ENOMEDIUM': '135', - 'ENOSHARE': '136', - 'ECASECLASH': '137', 'EILSEQ': '138', 'EOVERFLOW': '139', 'ECANCELED': '140', From 6036f604f5bda520212a3181eff2a46ed6d00450 Mon Sep 17 00:00:00 2001 From: Anthony Pesch Date: Fri, 9 Aug 2013 18:30:39 -0700 Subject: [PATCH 028/112] - fixed support for passing source to btest - added force_c option to btest --- tests/runner.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/tests/runner.py b/tests/runner.py index ddee682ee8b43..95ee39dab7ded 100755 --- a/tests/runner.py +++ b/tests/runner.py @@ -13706,15 +13706,17 @@ def book_path(*pathelems): self.btest(program, reference=book_path(basename.replace('.bc', '.png')), args=args) - def btest(self, filename, expected=None, reference=None, reference_slack=0, + def btest(self, filename, expected=None, reference=None, force_c=False, reference_slack=0, args=[], outfile='test.html', message='.'): # TODO: use in all other tests - filepath = path_from_root('tests', filename) - temp_filepath = os.path.join(self.get_dir(), os.path.basename(filename)) + # if we are provided the source and not a path, use that + filename_is_src = '\n' in filename + src = filename if filename_is_src else '' + filepath = path_from_root('tests', filename) if not filename_is_src else ('main.c' if force_c else 'main.cpp') + temp_filepath = os.path.join(self.get_dir(), os.path.basename(filepath)) + if filename_is_src: + with open(temp_filepath, 'w') as f: f.write(src) if not reference: - if '\n' in filename: # if we are provided the source and not a path, use that - src = filename - filename = 'main.cpp' - else: + if not src: with open(filepath) as f: src = f.read() with open(temp_filepath, 'w') as f: f.write(self.with_report_result(src)) else: From fec4930b0eb450e9c3892ebee77bacce82aac628 Mon Sep 17 00:00:00 2001 From: Anthony Pesch Date: Fri, 9 Aug 2013 19:55:48 -0700 Subject: [PATCH 029/112] removed pids_to_clean global --- tests/runner.py | 100 ++++++++++++++++++------------------------------ 1 file changed, 38 insertions(+), 62 deletions(-) diff --git a/tests/runner.py b/tests/runner.py index 95ee39dab7ded..31e3bb5ac47fb 100755 --- a/tests/runner.py +++ b/tests/runner.py @@ -13992,9 +13992,9 @@ def test_worker_api_2(self): def test_emscripten_async_wget2(self): self.btest('http.cpp', expected='0', args=['-I' + path_from_root('tests')]) - - pids_to_clean = [] - def clean_pids(self): + + @staticmethod + def clean_pids(pids): import signal, errno def pid_exists(pid): try: @@ -14015,15 +14015,15 @@ def kill_pids(pids, sig): except: print '[kill fail]' # ask nicely (to try and catch the children) - kill_pids(browser.pids_to_clean, signal.SIGTERM) + kill_pids(pids, signal.SIGTERM) time.sleep(1) # extreme prejudice, may leave children - kill_pids(browser.pids_to_clean, signal.SIGKILL) - browser.pids_to_clean = [] + kill_pids(pids, signal.SIGKILL) # Runs a websocket server at a specific port. port is the true tcp socket we forward to, port+1 is the websocket one class WebsockHarness: def __init__(self, port, server_func=None, no_server=False): + self.pids = [] self.port = port self.server_func = server_func self.no_server = no_server @@ -14046,15 +14046,15 @@ def server_func(q): server_queue = multiprocessing.Queue() self.server = multiprocessing.Process(target=server_func, args=(server_queue,)) self.server.start() - browser.pids_to_clean.append(self.server.pid) + self.pids.append(self.server.pid) while True: if not server_queue.empty(): spid = server_queue.get() if spid: - browser.pids_to_clean.append(spid) + self.pids.append(spid) break time.sleep(0.1) - print '[Socket server on processes %s]' % str(browser.pids_to_clean[-2:]) + print '[Socket server on processes %s]' % str(self.pids[-2:]) def websockify_func(wsp): wsp.start_server() @@ -14062,22 +14062,22 @@ def websockify_func(wsp): wsp.start_server() wsp = websockify.WebSocketProxy(verbose=True, listen_port=self.port+1, target_host="127.0.0.1", target_port=self.port, run_once=True) self.websockify = multiprocessing.Process(target=websockify_func, args=(wsp,)) self.websockify.start() - browser.pids_to_clean.append(self.websockify.pid) - print '[Websockify on processes %s]' % str(browser.pids_to_clean[-2:]) + self.pids.append(self.websockify.pid) + print '[Websockify on process %s]' % str(self.pids[-2:]) def __exit__(self, *args, **kwargs): if self.websockify.is_alive(): self.websockify.terminate() self.websockify.join() + # clean up any processes we started + browser.clean_pids(self.pids) + # always run these tests last # make sure to use different ports in each one because it takes a while for the processes to be cleaned up def test_sockets(self): - try: - with self.WebsockHarness(8990): - self.btest('sockets/test_sockets.c', expected='571', args=['-DSOCKK=8991']) - finally: - self.clean_pids() + with self.WebsockHarness(8990): + self.btest('sockets/test_sockets.c', expected='571', args=['-DSOCKK=8991']) def test_sockets_partial(self): def partial(q): @@ -14100,11 +14100,8 @@ def partial(q): csock.send("\x02\x01\x02") csock.send("\x01\x01") - try: - with self.WebsockHarness(8990, partial): - self.btest('sockets/test_sockets_partial.c', expected='165', args=['-DSOCKK=8991']) - finally: - self.clean_pids() + with self.WebsockHarness(8990, partial): + self.btest('sockets/test_sockets_partial.c', expected='165', args=['-DSOCKK=8991']) def make_relay_server(self, port1, port2): def relay_server(q): @@ -14117,30 +14114,21 @@ def relay_server(q): def test_sockets_bi(self): for datagram in [0,1]: for fileops in [0,1]: - try: - print >> sys.stderr, 'test_websocket_bi datagram %d, fileops %d' % (datagram, fileops) - with self.WebsockHarness(6992, self.make_relay_server(6992, 6994)): - with self.WebsockHarness(6994, no_server=True): - Popen([PYTHON, EMCC, path_from_root('tests', 'sockets/test_sockets_bi_side.c'), '-o', 'side.html', '-DSOCKK=6995', '-DTEST_DGRAM=%d' % datagram]).communicate() - self.btest('sockets/test_sockets_bi.c', expected='2499', args=['-DSOCKK=6993', '-DTEST_DGRAM=%d' % datagram, '-DTEST_FILE_OPS=%s' % fileops]) - finally: - self.clean_pids() + print >> sys.stderr, 'test_websocket_bi datagram %d, fileops %d' % (datagram, fileops) + with self.WebsockHarness(6992, self.make_relay_server(6992, 6994)): + with self.WebsockHarness(6994, no_server=True): + Popen([PYTHON, EMCC, path_from_root('tests', 'sockets/test_sockets_bi_side.c'), '-o', 'side.html', '-DSOCKK=6995', '-DTEST_DGRAM=%d' % datagram]).communicate() + self.btest('sockets/test_sockets_bi.c', expected='2499', args=['-DSOCKK=6993', '-DTEST_DGRAM=%d' % datagram, '-DTEST_FILE_OPS=%s' % fileops]) def test_sockets_gethostbyname(self): - try: - with self.WebsockHarness(7000): - self.btest('sockets/test_sockets_gethostbyname.c', expected='571', args=['-O2', '-DSOCKK=7001']) - finally: - self.clean_pids() + with self.WebsockHarness(7000): + self.btest('sockets/test_sockets_gethostbyname.c', expected='571', args=['-O2', '-DSOCKK=7001']) def test_sockets_bi_bigdata(self): - try: - with self.WebsockHarness(3992, self.make_relay_server(3992, 3994)): - with self.WebsockHarness(3994, no_server=True): - Popen([PYTHON, EMCC, path_from_root('tests', 'sockets/test_sockets_bi_side_bigdata.c'), '-o', 'side.html', '-DSOCKK=3995', '-s', 'SOCKET_DEBUG=0', '-I' + path_from_root('tests/sockets')]).communicate() - self.btest('sockets/test_sockets_bi_bigdata.c', expected='0', args=['-DSOCKK=3993', '-s', 'SOCKET_DEBUG=0', '-I' + path_from_root('tests/sockets')]) - finally: - self.clean_pids() + with self.WebsockHarness(3992, self.make_relay_server(3992, 3994)): + with self.WebsockHarness(3994, no_server=True): + Popen([PYTHON, EMCC, path_from_root('tests', 'sockets/test_sockets_bi_side_bigdata.c'), '-o', 'side.html', '-DSOCKK=3995', '-s', 'SOCKET_DEBUG=0', '-I' + path_from_root('tests/sockets')]).communicate() + self.btest('sockets/test_sockets_bi_bigdata.c', expected='0', args=['-DSOCKK=3993', '-s', 'SOCKET_DEBUG=0', '-I' + path_from_root('tests/sockets')]) def test_sockets_select_server_down(self): def closedServer(q): @@ -14149,11 +14137,8 @@ def closedServer(q): q.put(None) # No sub-process to start ssock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) ssock.bind(("127.0.0.1", 8994)) - try: - with self.WebsockHarness(8994, closedServer): - self.btest('sockets/test_sockets_select.c', expected='266', args=['-DSOCKK=8995']) - finally: - self.clean_pids() + with self.WebsockHarness(8994, closedServer): + self.btest('sockets/test_sockets_select.c', expected='266', args=['-DSOCKK=8995']) def test_sockets_select_server_closes_connection(self): def closingServer(q): @@ -14169,11 +14154,8 @@ def closingServer(q): csock.send("1234567") csock.close() - try: - with self.WebsockHarness(8994, closingServer): - self.btest('sockets/test_sockets_select_server_closes_connection.c', expected='266', args=['-DSOCKK=8995']) - finally: - self.clean_pids() + with self.WebsockHarness(8994, closingServer): + self.btest('sockets/test_sockets_select_server_closes_connection.c', expected='266', args=['-DSOCKK=8995']) def test_sockets_select_server_closes_connection_rw(self): def closingServer_rw(q): @@ -14201,11 +14183,8 @@ def closingServer_rw(q): csock.close() print "server: connection closed" - try: - with self.WebsockHarness(8998, closingServer_rw): - self.btest('sockets/test_sockets_select_server_closes_connection_rw.c', expected='266', args=['-DSOCKK=8999']) - finally: - self.clean_pids() + with self.WebsockHarness(8998, closingServer_rw): + self.btest('sockets/test_sockets_select_server_closes_connection_rw.c', expected='266', args=['-DSOCKK=8999']) def test_enet(self): try_delete(self.in_dir('enet')) @@ -14218,12 +14197,9 @@ def test_enet(self): os.chdir(pwd) Popen([PYTHON, EMCC, path_from_root('tests', 'sockets/test_enet_server.c'), '-o', 'server.html', '-DSOCKK=1235'] + enet).communicate() - try: - with self.WebsockHarness(1234, self.make_relay_server(1234, 1236)): - with self.WebsockHarness(1236, no_server=True): - self.btest('sockets/test_enet_client.c', expected='0', args=['-DSOCKK=1237'] + enet) - finally: - self.clean_pids() + with self.WebsockHarness(1234, self.make_relay_server(1234, 1236)): + with self.WebsockHarness(1236, no_server=True): + self.btest('sockets/test_enet_client.c', expected='0', args=['-DSOCKK=1237'] + enet) elif 'benchmark' in str(sys.argv): # Benchmarks. Run them with argument |benchmark|. To run a specific test, do From c6e5e9c37594bfcfe57551a0ea4336a81a53d49b Mon Sep 17 00:00:00 2001 From: Anthony Pesch Date: Fri, 9 Aug 2013 20:21:24 -0700 Subject: [PATCH 030/112] - removed test_sockets - removed test_sockets_bi_* - removed soon to be redundant test_sockets_select_server_closes_connection --- tests/runner.py | 41 +---- tests/sockets/test_sockets.c | 143 ------------------ tests/sockets/test_sockets_bi.c | 138 ----------------- tests/sockets/test_sockets_bi_bigdata.c | 138 ----------------- tests/sockets/test_sockets_bi_side.c | 93 ------------ tests/sockets/test_sockets_bi_side_bigdata.c | 90 ----------- tests/sockets/test_sockets_bigdata.h | 20 --- ..._sockets_select_server_closes_connection.c | 112 -------------- 8 files changed, 1 insertion(+), 774 deletions(-) delete mode 100644 tests/sockets/test_sockets.c delete mode 100644 tests/sockets/test_sockets_bi.c delete mode 100644 tests/sockets/test_sockets_bi_bigdata.c delete mode 100644 tests/sockets/test_sockets_bi_side.c delete mode 100644 tests/sockets/test_sockets_bi_side_bigdata.c delete mode 100644 tests/sockets/test_sockets_bigdata.h delete mode 100644 tests/sockets/test_sockets_select_server_closes_connection.c diff --git a/tests/runner.py b/tests/runner.py index 31e3bb5ac47fb..22cac2b02ee1b 100755 --- a/tests/runner.py +++ b/tests/runner.py @@ -12450,11 +12450,8 @@ def run_in_other_browser(url): i = sys.argv.index('browser') sys.argv = sys.argv[:i] + sys.argv[i+1:] sys.argv += [ - 'browser.test_sockets_bi', 'browser.test_sockets_gethostbyname', - 'browser.test_sockets_bi_bigdata', 'browser.test_sockets_select_server_down', - 'browser.test_sockets_select_server_closes_connection', 'browser.test_sockets_select_server_closes_connection_rw', 'browser.test_enet' ] @@ -14075,10 +14072,6 @@ def __exit__(self, *args, **kwargs): # always run these tests last # make sure to use different ports in each one because it takes a while for the processes to be cleaned up - def test_sockets(self): - with self.WebsockHarness(8990): - self.btest('sockets/test_sockets.c', expected='571', args=['-DSOCKK=8991']) - def test_sockets_partial(self): def partial(q): import socket @@ -14111,25 +14104,10 @@ def relay_server(q): proc.communicate() return relay_server - def test_sockets_bi(self): - for datagram in [0,1]: - for fileops in [0,1]: - print >> sys.stderr, 'test_websocket_bi datagram %d, fileops %d' % (datagram, fileops) - with self.WebsockHarness(6992, self.make_relay_server(6992, 6994)): - with self.WebsockHarness(6994, no_server=True): - Popen([PYTHON, EMCC, path_from_root('tests', 'sockets/test_sockets_bi_side.c'), '-o', 'side.html', '-DSOCKK=6995', '-DTEST_DGRAM=%d' % datagram]).communicate() - self.btest('sockets/test_sockets_bi.c', expected='2499', args=['-DSOCKK=6993', '-DTEST_DGRAM=%d' % datagram, '-DTEST_FILE_OPS=%s' % fileops]) - def test_sockets_gethostbyname(self): with self.WebsockHarness(7000): self.btest('sockets/test_sockets_gethostbyname.c', expected='571', args=['-O2', '-DSOCKK=7001']) - - def test_sockets_bi_bigdata(self): - with self.WebsockHarness(3992, self.make_relay_server(3992, 3994)): - with self.WebsockHarness(3994, no_server=True): - Popen([PYTHON, EMCC, path_from_root('tests', 'sockets/test_sockets_bi_side_bigdata.c'), '-o', 'side.html', '-DSOCKK=3995', '-s', 'SOCKET_DEBUG=0', '-I' + path_from_root('tests/sockets')]).communicate() - self.btest('sockets/test_sockets_bi_bigdata.c', expected='0', args=['-DSOCKK=3993', '-s', 'SOCKET_DEBUG=0', '-I' + path_from_root('tests/sockets')]) - + def test_sockets_select_server_down(self): def closedServer(q): import socket @@ -14140,23 +14118,6 @@ def closedServer(q): with self.WebsockHarness(8994, closedServer): self.btest('sockets/test_sockets_select.c', expected='266', args=['-DSOCKK=8995']) - def test_sockets_select_server_closes_connection(self): - def closingServer(q): - import socket - - q.put(None) # No sub-process to start - ssock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - ssock.bind(("127.0.0.1", 8994)) - ssock.listen(2) - while True: - csock, addr = ssock.accept() - print "Connection from %s" % repr(addr) - csock.send("1234567") - csock.close() - - with self.WebsockHarness(8994, closingServer): - self.btest('sockets/test_sockets_select_server_closes_connection.c', expected='266', args=['-DSOCKK=8995']) - def test_sockets_select_server_closes_connection_rw(self): def closingServer_rw(q): import socket diff --git a/tests/sockets/test_sockets.c b/tests/sockets/test_sockets.c deleted file mode 100644 index 8845ef43b2afa..0000000000000 --- a/tests/sockets/test_sockets.c +++ /dev/null @@ -1,143 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#if EMSCRIPTEN -#include -#endif - -#define EXPECTED_BYTES 5 - -int sockfd; -int not_always_data = 0; - -void finish(int result) { - close(sockfd); -#if EMSCRIPTEN - REPORT_RESULT(); -#endif - exit(result); -} - -unsigned int get_all_buf(int sock, char* output, unsigned int maxsize) { - // select check for IO - fd_set sett; - FD_ZERO(&sett); - assert(select(64, &sett, NULL, NULL, NULL) == 0); // empty set - FD_SET(sock, &sett); - assert(select(0, &sett, NULL, NULL, NULL) == 0); // max FD to check is 0 - assert(FD_ISSET(sock, &sett) == 0); - FD_SET(sock, &sett); - int select_says_yes = select(64, &sett, NULL, NULL, NULL); - - // ioctl check for IO - int bytes; - if (ioctl(sock, FIONREAD, &bytes) || bytes == 0) { - not_always_data = 1; - printf("ioctl says 0, FD_ISSET says %ld\n", FD_ISSET(sock, &sett)); - assert(FD_ISSET(sock, &sett) == 0); - return 0; - } - - assert(FD_ISSET(sock, &sett)); - assert(select_says_yes); // ioctl must agree with select - - char buffer[1024]; - int n; - unsigned int offset = 0; - while((errno = 0, (n = recv(sock, buffer, sizeof(buffer), 0))>0) || - errno == EINTR) { - if(n > 0) { - if (((unsigned int) n)+offset > maxsize) { - fprintf(stderr, "too much data!"); - finish(EXIT_FAILURE); - } - memcpy(output+offset, buffer, n); - offset += n; - } - } - - if (n < 0 && errno != EAGAIN && errno != EWOULDBLOCK) { - fprintf(stderr, "error in get_all_buf! %d", errno); - finish(EXIT_FAILURE); - } - return offset; -} - -void iter(void *arg) { - static char out[1024*2]; - static int pos = 0; - fd_set fdr; - int res; - - // make sure that sockfd is ready to read - FD_ZERO(&fdr); - FD_SET(sockfd, &fdr); - res = select(64, &fdr, NULL, NULL, NULL); - if (res == -1) { - perror("select failed"); - finish(EXIT_FAILURE); - } else if (!FD_ISSET(sockfd, &fdr)) { - return; - } - - // perform read write operations ... - int n = get_all_buf(sockfd, out+pos, 1024-pos); - if (n) printf("read! %d\n", n); - pos += n; - if (pos >= EXPECTED_BYTES) { - int i, sum = 0; - for (i=0; i < pos; i++) { - printf("%x\n", out[i]); - sum += out[i]; - } - - shutdown(sockfd, SHUT_RDWR); - - close(sockfd); - - printf("sum: %d\n", sum); - finish(sum); - } -} - -void main() { - struct sockaddr_in addr; - int res; - - sockfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); - if (sockfd == -1) { - perror("cannot create socket"); - finish(EXIT_FAILURE); - } - fcntl(sockfd, F_SETFL, O_NONBLOCK); - - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_port = htons(SOCKK); - if (inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr) != 1) { - perror("inet_pton failed"); - finish(EXIT_FAILURE); - } - - res = connect(sockfd, (struct sockaddr *)&addr, sizeof(addr)); - if (res == -1 && errno != EINPROGRESS) { - perror("connect failed"); - finish(EXIT_FAILURE); - } - -#if EMSCRIPTEN - emscripten_set_main_loop(iter, 0, 0); -#else - while (1) iter(NULL); -#endif -} - diff --git a/tests/sockets/test_sockets_bi.c b/tests/sockets/test_sockets_bi.c deleted file mode 100644 index 4266d20ce1e76..0000000000000 --- a/tests/sockets/test_sockets_bi.c +++ /dev/null @@ -1,138 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#if EMSCRIPTEN -#include -#endif - -#define EXPECTED_BYTES 28 - -int sockfd; - -void finish(int result) { - close(sockfd); -#if EMSCRIPTEN - REPORT_RESULT(); -#endif - exit(result); -} - -unsigned int get_all_buf(int sock, char* output, unsigned int maxsize) { - int bytes; - if (ioctl(sock, FIONREAD, &bytes)) return 0; - if (bytes == 0) return 0; - - char buffer[1024]; - int n; - unsigned int offset = 0; -#if TEST_FILE_OPS - while((errno = 0, (n = read(sock, buffer, sizeof(buffer)))>0) || -#else - while((errno = 0, (n = recv(sock, buffer, sizeof(buffer), 0))>0) || -#endif - errno == EINTR) { - if(n > 0) { - if (((unsigned int) n)+offset > maxsize) { - fprintf(stderr, "too much data!"); - finish(EXIT_FAILURE); - } - memcpy(output+offset, buffer, n); - offset += n; - } - } - - if(n < 0 && errno != EAGAIN && errno != EWOULDBLOCK) { - fprintf(stderr, "error in get_all_buf!"); - finish(EXIT_FAILURE); - } - return offset; -} - -void iter(void *arg) { - static char out[1024*2]; - static int pos = 0; - fd_set fdr; - int res; - - // make sure that sockfd is ready to read - FD_ZERO(&fdr); - FD_SET(sockfd, &fdr); - res = select(64, &fdr, NULL, NULL, NULL); - if (res == -1) { - perror("select failed"); - finish(EXIT_FAILURE); - } else if (!FD_ISSET(sockfd, &fdr)) { - return; - } - - // perform read write operations ... - int n = get_all_buf(sockfd, out+pos, 1024-pos); - if (n) printf("read! %d\n", n); - pos += n; - if (pos >= EXPECTED_BYTES) { - int i, sum = 0; - for (i=0; i < pos; i++) { - printf("%x\n", out[i]); - sum += out[i]; - } - - shutdown(sockfd, SHUT_RDWR); - close(sockfd); - - printf("sum: %d\n", sum); - finish(sum); - } -} - -int main() { - struct sockaddr_in addr; - int res; - - printf("hello from main page\n"); - -#if !TEST_DGRAM - sockfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); -#else - sockfd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); -#endif - if (sockfd == -1) { - perror("cannot create socket"); - finish(EXIT_FAILURE); - } - fcntl(sockfd, F_SETFL, O_NONBLOCK); - - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_port = htons(SOCKK); - if (inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr) != 1) { - perror("inet_pton failed"); - finish(EXIT_FAILURE); - } - - res = connect(sockfd, (struct sockaddr *)&addr, sizeof(addr)); - if (res == -1 && errno != EINPROGRESS) { - perror("connect failed"); - finish(EXIT_FAILURE); - } - -#if EMSCRIPTEN - emscripten_run_script("console.log('adding iframe');" - "var iframe = document.createElement('iframe');" - "iframe.src = 'side.html';" - "document.body.appendChild(iframe);" - "console.log('added.');"); - emscripten_set_main_loop(iter, 0, 0); -#else - while (1) iter(NULL); -#endif - - return EXIT_SUCCESS; -} diff --git a/tests/sockets/test_sockets_bi_bigdata.c b/tests/sockets/test_sockets_bi_bigdata.c deleted file mode 100644 index c1d8100e6c926..0000000000000 --- a/tests/sockets/test_sockets_bi_bigdata.c +++ /dev/null @@ -1,138 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#if EMSCRIPTEN -#include -#endif - -#include "test_sockets_bigdata.h" - -#define EXPECTED_BYTES DATA_SIZE - -int sockfd; - -void finish(int result) { - close(sockfd); -#if EMSCRIPTEN - REPORT_RESULT(); -#endif - exit(result); -} - -unsigned int get_all_buf(int sock, char* output, unsigned int maxsize) { - int bytes; - if (ioctl(sock, FIONREAD, &bytes)) return 0; - if (bytes == 0) return 0; - - char buffer[EXPECTED_BYTES]; - int n; - unsigned int offset = 0; - while((errno = 0, (n = recv(sock, buffer, sizeof(buffer), 0))>0) || - errno == EINTR) { - if (n > 0) { - if (((unsigned int) n)+offset > maxsize) { - fprintf(stderr, "too much data!"); - finish(EXIT_FAILURE); - } - memcpy(output+offset, buffer, n); - offset += n; - } - } - - if (n < 0 && errno != EAGAIN && errno != EWOULDBLOCK) { - fprintf(stderr, "error in get_all_buf!"); - finish(EXIT_FAILURE); - } - return offset; -} - -void iter(void *arg) { - static char out[EXPECTED_BYTES]; - static int pos = 0; - fd_set fdr; - int res; - - // make sure that sockfd has finished connecting and is ready to read - FD_ZERO(&fdr); - FD_SET(sockfd, &fdr); - res = select(64, &fdr, NULL, NULL, NULL); - if (res == -1) { - perror("select failed"); - finish(EXIT_FAILURE); - return; - } else if (!FD_ISSET(sockfd, &fdr)) { - return; - } - - // perform read write operations ... - printf("so far %d, expecting up to %d\n", pos, EXPECTED_BYTES-pos); - res = get_all_buf(sockfd, out+pos, EXPECTED_BYTES-pos); - if (res) printf("read! %d\n", res); - pos += res; - if (pos >= EXPECTED_BYTES) { - shutdown(sockfd, SHUT_RDWR); - - close(sockfd); - - char *comp = generateData(); - int result = strcmp(comp, out); - if (result != 0) { - for (int i = 0; i < DATA_SIZE; i++) { - printf("%d:%d\n", comp[i], out[i]); - } - } - finish(result); - } -} - -int main() { - struct sockaddr_in addr; - int res; - - printf("hello from main page\n"); - - sockfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); - if (sockfd == -1) { - perror("cannot create socket"); - finish(EXIT_FAILURE); - } - fcntl(sockfd, F_SETFL, O_NONBLOCK); - - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_port = htons(SOCKK); - if (inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr) != 1) { - perror("inet_pton failed"); - finish(EXIT_FAILURE); - } - - res = connect(sockfd, (struct sockaddr *)&addr, sizeof(addr)); - if (res == -1 && errno != EINPROGRESS) { - perror("connect failed"); - finish(EXIT_FAILURE); - } - -#if EMSCRIPTEN - emscripten_run_script("console.log('adding iframe');" - "var iframe = document.createElement('iframe');" - "iframe.src = 'side.html';" - "iframe.width = '100%';" - "iframe.width = '40%';" - "document.body.appendChild(iframe);" - "console.log('added.');"); - emscripten_set_main_loop(iter, 3, 0); -#else - while (1) iter(NULL); -#endif - - return EXIT_SUCCESS; -} - diff --git a/tests/sockets/test_sockets_bi_side.c b/tests/sockets/test_sockets_bi_side.c deleted file mode 100644 index b891063289b96..0000000000000 --- a/tests/sockets/test_sockets_bi_side.c +++ /dev/null @@ -1,93 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#if EMSCRIPTEN -#include -#endif - -#define EXPECTED_BYTES 5 - -int sockfd = -1; - -void finish(int result) { - close(sockfd); - exit(result); -} - -void loop() { - fd_set fdw; - int res; - - // Make sure that sockfd has actually finished connecting - // and is ready to read. - FD_ZERO(&fdw); - FD_SET(sockfd, &fdw); - res = select(64, NULL, &fdw, NULL, NULL); - if (res == -1) { - perror("select failed"); - finish(EXIT_FAILURE); - } else if (!FD_ISSET(sockfd, &fdw)) { - return; - } - - char data[] = "hello from the other siide\n"; - - printf("send..\n"); -#if TEST_FILE_OPS - res = write(sockfd, data, sizeof(data)); -#else - res = send(sockfd, data, sizeof(data), 0); -#endif - if (res == -1) { - if (errno != EAGAIN) { - perror("send error"); - finish(EXIT_FAILURE); - } - return; - } - - finish(EXIT_SUCCESS); -} - -int main() { - struct sockaddr_in addr; - int res; -#if !TEST_DGRAM - sockfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); -#else - sockfd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); -#endif - if (sockfd == -1) { - perror("cannot create socket"); - finish(EXIT_FAILURE); - } - fcntl(sockfd, F_SETFL, O_NONBLOCK); - - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_port = htons(SOCKK); - if (inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr) != 1) { - perror("inet_pton failed"); - finish(EXIT_FAILURE); - } - - res = connect(sockfd, (struct sockaddr *)&addr, sizeof(addr)); - if (res == -1 && errno != EINPROGRESS) { - perror("connect failed"); - finish(EXIT_FAILURE); - } - - emscripten_set_main_loop(loop, 0, 0); - - return EXIT_SUCCESS; -} - diff --git a/tests/sockets/test_sockets_bi_side_bigdata.c b/tests/sockets/test_sockets_bi_side_bigdata.c deleted file mode 100644 index e31029b635c76..0000000000000 --- a/tests/sockets/test_sockets_bi_side_bigdata.c +++ /dev/null @@ -1,90 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#if EMSCRIPTEN -#include -#endif - -#include "test_sockets_bigdata.h" - -#define EXPECTED_BYTES 5 - -int sockfd = -1; -char *data = NULL; - -void finish(int result) { - close(sockfd); - exit(result); -} - -void loop() { - fd_set fdw; - int res; - - // make sure that sockfd has finished connecting and is ready to write - FD_ZERO(&fdw); - FD_SET(sockfd, &fdw); - res = select(64, NULL, &fdw, NULL, NULL); - if (res == -1) { - perror("select failed"); - finish(EXIT_FAILURE); - return; - } else if (!FD_ISSET(sockfd, &fdw)) { - return; - } - - printf("send..\n"); - - res = send(sockfd, data, DATA_SIZE, 0); - if (res == -1) { - if (errno != EAGAIN) { - perror("send error"); - finish(EXIT_FAILURE); - } - return; - } - - finish(EXIT_SUCCESS); -} - -int main() { - struct sockaddr_in addr; - int res; - - sockfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); - if (sockfd == -1) { - perror("cannot create socket"); - exit(EXIT_FAILURE); - } - fcntl(sockfd, F_SETFL, O_NONBLOCK); - - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_port = htons(SOCKK); - if (inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr) != 1) { - perror("inet_pton failed"); - finish(EXIT_FAILURE); - } - - printf("connect..\n"); - - res = connect(sockfd, (struct sockaddr *)&addr, sizeof(addr)); - if (res == -1 && errno != EINPROGRESS) { - perror("connect failed"); - finish(EXIT_FAILURE); - } - - data = generateData(); - - emscripten_set_main_loop(loop, 1, 0); - - return EXIT_SUCCESS; -} \ No newline at end of file diff --git a/tests/sockets/test_sockets_bigdata.h b/tests/sockets/test_sockets_bigdata.h deleted file mode 100644 index 17149ad6f0f99..0000000000000 --- a/tests/sockets/test_sockets_bigdata.h +++ /dev/null @@ -1,20 +0,0 @@ - -#include - -#define DATA_SIZE (256*256*2) -// 1500 fails - -char *generateData() { - char *ret = malloc(256*256*2); - char *curr = ret; - for (int i = 0; i < 256; i++) { - for (int j = 0; j < 256; j++) { - *curr = i; - curr++; - *curr = j; - curr++; - } - } - return ret; -} - diff --git a/tests/sockets/test_sockets_select_server_closes_connection.c b/tests/sockets/test_sockets_select_server_closes_connection.c deleted file mode 100644 index 4181b12bbc5c6..0000000000000 --- a/tests/sockets/test_sockets_select_server_closes_connection.c +++ /dev/null @@ -1,112 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#if EMSCRIPTEN -#include -#endif - -#define EXPECTED_BYTES 5 - -int sockfd = -1; - -void finish(int result) { - close(sockfd); -#if EMSCRIPTEN - REPORT_RESULT(); -#endif - exit(result); -} - -void iter(void *arg) { - static char readbuf[1024]; - static int readPos = 0; - - fd_set sett; - FD_ZERO(&sett); - FD_SET(sockfd, &sett); - - int res = select(64, &sett, NULL, NULL, NULL); - - if (res == -1) { - perror("select failed"); - finish(EXIT_FAILURE); - } else if (res == 0) { - return; - } else if (res > 0) { - assert(FD_ISSET(sockfd, &sett)); - - int bytesRead = recv(sockfd, readbuf+readPos, 7-readPos, 0); - if (bytesRead == -1) { - if (errno != EAGAIN) { - perror("recv error"); - finish(EXIT_FAILURE); - } - // try again - return; - } - - if (readPos < 7) { - readPos += bytesRead; - } else { - if (!bytesRead) { - perror("Connection to websocket server was closed as expected"); - finish(266); - } else { - perror("Connection to websocket server was not closed"); - finish(EXIT_FAILURE); - } - } - } - - return; -} - -// Scenario: the server sends data and closes the connection after 7 bytes. -// This test should provoke the situation in which the underlying -// tcp connection has been torn down already but there is still data -// in the queue. The select call has to succeed as long the queue -// still contains data and only then start to throw errors. -int main() { - struct sockaddr_in addr; - int res; - - sockfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); - if (sockfd == -1) { - perror("cannot create socket"); - finish(EXIT_FAILURE); - } - fcntl(sockfd, F_SETFL, O_NONBLOCK); - - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_port = htons(SOCKK); - if (inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr) != 1) { - perror("inet_pton failed"); - finish(EXIT_FAILURE); - } - - // This call should succeed (even if the server port is closed) - res = connect(sockfd, (struct sockaddr *)&addr, sizeof(addr)); - if (res == -1 && errno != EINPROGRESS) { - perror("connect failed"); - finish(EXIT_FAILURE); - } - -#if EMSCRIPTEN - emscripten_set_main_loop(iter, 0, 0); -#else - while (1) iter(NULL); -#endif - - return EXIT_FAILURE; -} - From 4ae44dc8d0e8370d92ff4601ccb6cd59e7f0217f Mon Sep 17 00:00:00 2001 From: Anthony Pesch Date: Fri, 9 Aug 2013 20:44:58 -0700 Subject: [PATCH 031/112] updated and temporarily disabled test_sockets_gethostbyname --- tests/runner.py | 7 +- tests/sockets/test_sockets_gethostbyname.c | 131 ++++----------------- 2 files changed, 24 insertions(+), 114 deletions(-) diff --git a/tests/runner.py b/tests/runner.py index 22cac2b02ee1b..5a82d8f8c5db1 100755 --- a/tests/runner.py +++ b/tests/runner.py @@ -12450,7 +12450,6 @@ def run_in_other_browser(url): i = sys.argv.index('browser') sys.argv = sys.argv[:i] + sys.argv[i+1:] sys.argv += [ - 'browser.test_sockets_gethostbyname', 'browser.test_sockets_select_server_down', 'browser.test_sockets_select_server_closes_connection_rw', 'browser.test_enet' @@ -14104,9 +14103,9 @@ def relay_server(q): proc.communicate() return relay_server - def test_sockets_gethostbyname(self): - with self.WebsockHarness(7000): - self.btest('sockets/test_sockets_gethostbyname.c', expected='571', args=['-O2', '-DSOCKK=7001']) + # TODO add support for gethostbyaddr to re-enable this test + # def test_sockets_gethostbyname(self): + # self.btest('sockets/test_sockets_gethostbyname.c', expected='0', args=['-O2', '-DSOCKK=8997']) def test_sockets_select_server_down(self): def closedServer(q): diff --git a/tests/sockets/test_sockets_gethostbyname.c b/tests/sockets/test_sockets_gethostbyname.c index 59c55ba153820..12fc6d9d9e778 100644 --- a/tests/sockets/test_sockets_gethostbyname.c +++ b/tests/sockets/test_sockets_gethostbyname.c @@ -1,20 +1,16 @@ -#include -#include -#include -#include #include +#include +#include +#include +#include #include #include #include #include -#include -#include #if EMSCRIPTEN #include #endif -#define EXPECTED_BYTES 5 - int sockfd; void finish(int result) { @@ -25,113 +21,28 @@ void finish(int result) { exit(result); } -unsigned int get_all_buf(int sock, char* output, unsigned int maxsize) { - int bytes; - if (ioctl(sock, FIONREAD, &bytes)) return 0; - if (bytes == 0) return 0; - - char buffer[1024]; - int n; - unsigned int offset = 0; - while((errno = 0, (n = recv(sock, buffer, sizeof(buffer), 0))>0) || - errno == EINTR) { - if (n > 0) { - if (((unsigned int) n)+offset > maxsize) { - fprintf(stderr, "too much data!"); - finish(EXIT_FAILURE); - } - memcpy(output+offset, buffer, n); - offset += n; - } - } - - if (n < 0 && errno != EAGAIN && errno != EWOULDBLOCK) { - perror("error in get_all_buf!"); - finish(EXIT_FAILURE); - } - return offset; -} - -void iter() { - static char out[1024*2]; - static int pos = 0; - fd_set fdr; - int res; - - // make sure that sockfd has finished connecting and is ready to read - FD_ZERO(&fdr); - FD_SET(sockfd, &fdr); - res = select(64, &fdr, NULL, NULL, NULL); - if (res == -1) { - perror("select failed"); - finish(EXIT_FAILURE); - } else if (!FD_ISSET(sockfd, &fdr)) { - return; - } - - // perform read write operations ... - int n = get_all_buf(sockfd, out+pos, 1024-pos); - if (n) printf("read! %d\n", n); - pos += n; - if (pos >= EXPECTED_BYTES) { - int i, sum = 0; - for (i=0; i < pos; i++) { - printf("%x\n", out[i]); - sum += out[i]; - } - - shutdown(sockfd, SHUT_RDWR); - - close(sockfd); - - printf("sum: %d\n", sum); - - finish(sum); - } -} - int main() { - struct sockaddr_in addr; - int res; - - sockfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); - if (sockfd == -1) { - perror("cannot create socket"); - finish(EXIT_FAILURE); - } - fcntl(sockfd, F_SETFL, O_NONBLOCK); - - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_port = htons(SOCKK); - struct hostent *host0 = gethostbyname("test.com"); // increment hostname counter to check for possible but at 0,0 not differentiating low/high - struct hostent *host = gethostbyname("localhost"); + char str[INET_ADDRSTRLEN]; + struct in_addr addr; + const char *res; + int err; + + // resolve the hostname ot an actual address + struct hostent *host = gethostbyname("slashdot.org"); + + // convert the raw address to a string char **raw_addr_list = host->h_addr_list; int *raw_addr = (int*)*raw_addr_list; - printf("raw addr: %d\n", *raw_addr); - char name[INET_ADDRSTRLEN]; - if (!inet_ntop(AF_INET, raw_addr, name, sizeof(name))) { - printf("could not figure out name\n"); - finish(EXIT_FAILURE); - } - printf("localhost has 'ip' of %s\n", name); - - if (inet_pton(AF_INET, name, &addr.sin_addr) != 1) { - perror("inet_pton failed"); - finish(EXIT_FAILURE); - } + res = inet_ntop(host->h_addrtype, raw_addr, str, INET_ADDRSTRLEN); + assert(res); - res = connect(sockfd, (struct sockaddr *)&addr, sizeof(addr)); - if (res == -1 && errno != EINPROGRESS) { - perror("connect failed"); - finish(EXIT_FAILURE); - } + // convert the string to an in_addr structure + err = inet_pton(AF_INET, str, &addr); + assert(err == 1); -#if EMSCRIPTEN - emscripten_set_main_loop(iter, 0, 0); -#else - while (1) iter(); -#endif + // do a reverse lookup on the ip address + struct hostent *host1 = gethostbyaddr(&addr, sizeof(addr), host->h_addrtype); + assert(strstr(host1->h_name, "slashdot.org")); return EXIT_SUCCESS; } From 36d62242f8172eefc01b30fdf248dc30100dc141 Mon Sep 17 00:00:00 2001 From: Anthony Pesch Date: Fri, 9 Aug 2013 21:01:08 -0700 Subject: [PATCH 032/112] - converted all socket tests to use C for the server-side - added new echo client / server test - updated websock harness and added new CompiledServerHarness to help run tests on multiple backends --- tests/runner.py | 259 ++++++++++-------- tests/sockets/test_sockets_echo_client.c | 148 ++++++++++ tests/sockets/test_sockets_echo_server.c | 152 ++++++++++ tests/sockets/test_sockets_msg.h | 78 ++++++ ...artial.c => test_sockets_partial_client.c} | 2 +- tests/sockets/test_sockets_partial_server.c | 130 +++++++++ ...lect_server_closes_connection_client_rw.c} | 42 +-- ..._sockets_select_server_no_accept_client.c} | 0 ...t_sockets_select_server_no_accept_server.c | 86 ++++++ 9 files changed, 769 insertions(+), 128 deletions(-) create mode 100644 tests/sockets/test_sockets_echo_client.c create mode 100644 tests/sockets/test_sockets_echo_server.c create mode 100644 tests/sockets/test_sockets_msg.h rename tests/sockets/{test_sockets_partial.c => test_sockets_partial_client.c} (98%) create mode 100644 tests/sockets/test_sockets_partial_server.c rename tests/sockets/{test_sockets_select_server_closes_connection_rw.c => test_sockets_select_server_closes_connection_client_rw.c} (84%) rename tests/sockets/{test_sockets_select.c => test_sockets_select_server_no_accept_client.c} (100%) create mode 100644 tests/sockets/test_sockets_select_server_no_accept_server.c diff --git a/tests/runner.py b/tests/runner.py index 5a82d8f8c5db1..3a8e87512ba00 100755 --- a/tests/runner.py +++ b/tests/runner.py @@ -12450,7 +12450,10 @@ def run_in_other_browser(url): i = sys.argv.index('browser') sys.argv = sys.argv[:i] + sys.argv[i+1:] sys.argv += [ - 'browser.test_sockets_select_server_down', + 'browser.test_sockets_echo', + 'browser.test_sockets_echo_bigdata', + 'browser.test_sockets_partial', + 'browser.test_sockets_select_server_no_accept', 'browser.test_sockets_select_server_closes_connection_rw', 'browser.test_enet' ] @@ -13994,10 +13997,10 @@ def clean_pids(pids): import signal, errno def pid_exists(pid): try: - # NOTE: may just kill the process in Windows - os.kill(pid, 0) + # NOTE: may just kill the process in Windows + os.kill(pid, 0) except OSError, e: - return e.errno == errno.EPERM + return e.errno == errno.EPERM else: return True def kill_pids(pids, sig): @@ -14016,52 +14019,35 @@ def kill_pids(pids, sig): # extreme prejudice, may leave children kill_pids(pids, signal.SIGKILL) - # Runs a websocket server at a specific port. port is the true tcp socket we forward to, port+1 is the websocket one - class WebsockHarness: - def __init__(self, port, server_func=None, no_server=False): + class WebsockifyServerHarness: + def __init__(self, filename, args, listen_port, target_port): self.pids = [] - self.port = port - self.server_func = server_func - self.no_server = no_server + self.filename = filename + self.target_port = target_port + self.listen_port = listen_port + self.args = args or [] def __enter__(self): import socket, websockify - if not self.no_server: - def server_func(q): - q.put(None) # No sub-process to start - ssock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - ssock.bind(("127.0.0.1", self.port)) - ssock.listen(2) - while True: - csock, addr = ssock.accept() - print "Connection from %s" % repr(addr) - csock.send("te\x01\xff\x79st\x02") - - server_func = self.server_func or server_func - - server_queue = multiprocessing.Queue() - self.server = multiprocessing.Process(target=server_func, args=(server_queue,)) - self.server.start() - self.pids.append(self.server.pid) - while True: - if not server_queue.empty(): - spid = server_queue.get() - if spid: - self.pids.append(spid) - break - time.sleep(0.1) - print '[Socket server on processes %s]' % str(self.pids[-2:]) - - def websockify_func(wsp): wsp.start_server() - print >> sys.stderr, 'running websockify on %d, forward to tcp %d' % (self.port+1, self.port) - wsp = websockify.WebSocketProxy(verbose=True, listen_port=self.port+1, target_host="127.0.0.1", target_port=self.port, run_once=True) - self.websockify = multiprocessing.Process(target=websockify_func, args=(wsp,)) + # compile the server + # NOTE empty filename support is a hack to support + # the current test_enet + if self.filename: + Popen([CLANG_CC, path_from_root('tests', self.filename), '-o', 'server'] + self.args).communicate() + process = Popen(['./server']) + self.pids.append(process.pid) + + # start the websocket proxy + print >> sys.stderr, 'running websockify on %d, forward to tcp %d' % (self.listen_port, self.target_port) + wsp = websockify.WebSocketProxy(verbose=True, listen_port=self.listen_port, target_host="127.0.0.1", target_port=self.target_port, run_once=True) + self.websockify = multiprocessing.Process(target=wsp.start_server) self.websockify.start() self.pids.append(self.websockify.pid) print '[Websockify on process %s]' % str(self.pids[-2:]) def __exit__(self, *args, **kwargs): + # try to kill the websockify proxy gracefully if self.websockify.is_alive(): self.websockify.terminate() self.websockify.join() @@ -14069,83 +14055,102 @@ def __exit__(self, *args, **kwargs): # clean up any processes we started browser.clean_pids(self.pids) + + class CompiledServerHarness: + def __init__(self, filename, args): + self.pids = browser.PidContainer() + self.filename = filename + self.args = args or [] + + def __enter__(self): + import socket, websockify + + # compile the server + Popen([PYTHON, EMCC, path_from_root('tests', self.filename), '-o', 'server.js'] + self.args).communicate() + process = Popen([NODE_JS, 'server.js']) + self.pids.append(process.pid) + + def __exit__(self, *args, **kwargs): + # clean up any processes we started + self.pids.kill_all() + + def make_relay_server(self, port1, port2): + print >> sys.stderr, 'creating relay server on ports %d,%d' % (port1, port2) + proc = Popen([PYTHON, path_from_root('tests', 'sockets/socket_relay.py'), str(port1), str(port2)]) + return proc + # always run these tests last # make sure to use different ports in each one because it takes a while for the processes to be cleaned up - def test_sockets_partial(self): - def partial(q): - import socket - - q.put(None) # No sub-process to start - ssock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - ssock.bind(("127.0.0.1", 8990)) - ssock.listen(2) - while True: - csock, addr = ssock.accept() - print "Connection from %s" % repr(addr) - csock.send("\x09\x01\x02\x03\x04\x05\x06\x07\x08\x09") - csock.send("\x08\x01\x02\x03\x04\x05\x06\x07\x08") - csock.send("\x07\x01\x02\x03\x04\x05\x06\x07") - csock.send("\x06\x01\x02\x03\x04\x05\x06") - csock.send("\x05\x01\x02\x03\x04\x05") - csock.send("\x04\x01\x02\x03\x04") - csock.send("\x03\x01\x02\x03") - csock.send("\x02\x01\x02") - csock.send("\x01\x01") - - with self.WebsockHarness(8990, partial): - self.btest('sockets/test_sockets_partial.c', expected='165', args=['-DSOCKK=8991']) - def make_relay_server(self, port1, port2): - def relay_server(q): - print >> sys.stderr, 'creating relay server on ports %d,%d' % (port1, port2) - proc = Popen([PYTHON, path_from_root('tests', 'sockets/socket_relay.py'), str(port1), str(port2)]) - q.put(proc.pid) - proc.communicate() - return relay_server + # NOTE all datagram tests are temporarily disabled, as + # we can't truly test datagram sockets until we have + # proper listen server support. + + def test_sockets_echo(self): + sockets_include = '-I'+path_from_root('tests/sockets') + + for datagram in [0]: + dgram_define = '-DTEST_DGRAM=%d' % datagram + + for harness in [ + self.WebsockifyServerHarness('sockets/test_sockets_echo_server.c', ['-DSOCKK=8990', dgram_define, sockets_include], 8991, 8990) + # self.CompiledServerHarness('sockets/test_sockets_echo_server.c', ['-DSOCKK=8990', dgram_define, sockets_include]) + ]: + with harness: + self.btest('sockets/test_sockets_echo_client.c', expected='0', args=['-DSOCKK=8991', dgram_define, sockets_include]) + + def test_sockets_echo_bigdata(self): + sockets_include = '-I'+path_from_root('tests/sockets') + + for datagram in [0]: + dgram_define = '-DTEST_DGRAM=%d' % datagram + + # generate a large string literal to use as our message + message = '' + for i in range(256*256*2): + message += str(unichr(ord('a') + (i % 26))) + + # re-write the client test with this literal (it's too big to pass via command line) + input_filename = path_from_root('tests/sockets', 'test_sockets_echo_client.c') + input = open(input_filename).read() + output = input.replace('#define MESSAGE "pingtothepong"', '#define MESSAGE "%s"' % message) + + for harness in [ + self.WebsockifyServerHarness('sockets/test_sockets_echo_server.c', ['-DSOCKK=8992', dgram_define, sockets_include], 8993, 8992) + ]: + with harness: + self.btest(output, expected='0', args=['-DSOCKK=8993', dgram_define, sockets_include], force_c=True) + + def test_sockets_partial(self): + for harness in [ + self.WebsockifyServerHarness('sockets/test_sockets_partial_server.c', ['-DSOCKK=8994'], 8995, 8994) + ]: + with harness: + self.btest('sockets/test_sockets_partial_client.c', expected='165', args=['-DSOCKK=8995']) # TODO add support for gethostbyaddr to re-enable this test # def test_sockets_gethostbyname(self): # self.btest('sockets/test_sockets_gethostbyname.c', expected='0', args=['-O2', '-DSOCKK=8997']) - - def test_sockets_select_server_down(self): - def closedServer(q): - import socket - q.put(None) # No sub-process to start - ssock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - ssock.bind(("127.0.0.1", 8994)) - with self.WebsockHarness(8994, closedServer): - self.btest('sockets/test_sockets_select.c', expected='266', args=['-DSOCKK=8995']) + def test_sockets_select_server_no_accept(self): + for harness in [ + self.WebsockifyServerHarness('sockets/test_sockets_select_server_no_accept_server.c', ['-DSOCKK=8995'], 8996, 8995) + ]: + self.btest('sockets/test_sockets_select_server_no_accept_client.c', expected='266', args=['-DSOCKK=8996']) def test_sockets_select_server_closes_connection_rw(self): - def closingServer_rw(q): - import socket - - q.put(None) # No sub-process to start - ssock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - ssock.bind(("127.0.0.1", 8998)) - ssock.listen(2) - while True: - csock, addr = ssock.accept() - print "Connection from %s" % repr(addr) - readArray = bytearray(10) - #readBuffer = buffer(readArray) - bytesRead = 0 - # Let the client start to write data - while (bytesRead < 10): - (readBytes, address) = csock.recvfrom_into( readArray, 10 ) - bytesRead += readBytes - print "server: 10 bytes read" - # Now we write a message on our own ... - csock.send("0123456789") - print "server: 10 bytes written" - # And immediately close the connection - csock.close() - print "server: connection closed" - - with self.WebsockHarness(8998, closingServer_rw): - self.btest('sockets/test_sockets_select_server_closes_connection_rw.c', expected='266', args=['-DSOCKK=8999']) + sockets_include = '-I'+path_from_root('tests/sockets') + for harness in [ + self.WebsockifyServerHarness('sockets/test_sockets_echo_server.c', ['-DSOCKK=9004', sockets_include], 9005, 9004) + ]: + with harness: + self.btest('sockets/test_sockets_select_server_closes_connection_client_rw.c', expected='266', args=['-DSOCKK=9005', sockets_include]) + + # TODO remove this once we have proper listen server support built into emscripten. + # being that enet uses datagram sockets, we can't proxy to a native server with + # websockify, so we're emulating the listen server in the browser and relaying + # between two TCP servers. def test_enet(self): try_delete(self.in_dir('enet')) shutil.copytree(path_from_root('tests', 'enet'), self.in_dir('enet')) @@ -14155,11 +14160,45 @@ def test_enet(self): Popen([PYTHON, path_from_root('emmake'), 'make']).communicate() enet = [self.in_dir('enet', '.libs', 'libenet.a'), '-I'+path_from_root('tests', 'enet', 'include')] os.chdir(pwd) - Popen([PYTHON, EMCC, path_from_root('tests', 'sockets/test_enet_server.c'), '-o', 'server.html', '-DSOCKK=1235'] + enet).communicate() + Popen([PYTHON, EMCC, path_from_root('tests/sockets', 'test_enet_server.c'), '-o', 'server.html', '-DSOCKK=2235'] + enet).communicate() - with self.WebsockHarness(1234, self.make_relay_server(1234, 1236)): - with self.WebsockHarness(1236, no_server=True): - self.btest('sockets/test_enet_client.c', expected='0', args=['-DSOCKK=1237'] + enet) + with self.WebsockifyServerHarness('', [], 2235, 2234): + with self.WebsockifyServerHarness('', [], 2237, 2236): + pids = [] + try: + proc = self.make_relay_server(2234, 2236) + pids.append(proc.pid) + self.btest('sockets/test_enet_client.c', expected='0', args=['-DSOCKK=2237'] + enet) + finally: + browser.clean_pids(pids); + + # TODO use this once we have listen server support + # def test_enet(self): + # # build a native version of the enet lib + # try_delete(self.in_dir('enet_native')) + # shutil.copytree(path_from_root('tests', 'enet'), self.in_dir('enet_native')) + # pwd = os.getcwd() + # os.chdir(self.in_dir('enet_native')) + # Popen(['./configure']).communicate() + # Popen(['make']).communicate() + # enet_native = [self.in_dir('enet_native', '.libs', 'libenet.a'), '-I'+path_from_root('tests', 'enet', 'include')] + # os.chdir(pwd) + + # # build a transpiled version of enet lib + # try_delete(self.in_dir('enet_emscripten')) + # shutil.copytree(path_from_root('tests', 'enet'), self.in_dir('enet_emscripten')) + # pwd = os.getcwd() + # os.chdir(self.in_dir('enet_emscripten')) + # Popen([PYTHON, path_from_root('emconfigure'), './configure']).communicate() + # Popen([PYTHON, path_from_root('emmake'), 'make']).communicate() + # enet_emscripten = [self.in_dir('enet_emscripten', '.libs', 'libenet.a'), '-I'+path_from_root('tests', 'enet', 'include')] + # os.chdir(pwd) + + # for harness in [ + # self.CompiledServerHarness('sockets/test_enet_server.c', ['-DSOCKK=9010'] + enet_native, 9011, 9010) + # ]: + # with harness: + # self.btest('sockets/test_enet_client.c', expected='0', args=['-DSOCKK=9011'] + enet_emscripten) elif 'benchmark' in str(sys.argv): # Benchmarks. Run them with argument |benchmark|. To run a specific test, do diff --git a/tests/sockets/test_sockets_echo_client.c b/tests/sockets/test_sockets_echo_client.c new file mode 100644 index 0000000000000..6b3ccef34d1fd --- /dev/null +++ b/tests/sockets/test_sockets_echo_client.c @@ -0,0 +1,148 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if EMSCRIPTEN +#include +#endif + +#include "test_sockets_msg.h" + +// message to send to the server +#ifndef MESSAGE +#define MESSAGE "pingtothepong" +#endif + +typedef enum { + MSG_READ, + MSG_WRITE +} msg_state_t; + +typedef struct { + int fd; + msg_t msg; + msg_state_t state; +} server_t; + +server_t server; +msg_t echo_msg; +int echo_read; +int echo_wrote; + +void finish(int result) { + close(server.fd); +#if EMSCRIPTEN + REPORT_RESULT(); +#endif + exit(result); +} + +void main_loop(void *arg) { + static char out[1024*2]; + static int pos = 0; + fd_set fdr; + fd_set fdw; + int res; + + // make sure that server.fd is ready to read / write + FD_ZERO(&fdr); + FD_ZERO(&fdw); + FD_SET(server.fd, &fdr); + FD_SET(server.fd, &fdw); + res = select(64, &fdr, &fdw, NULL, NULL); + if (res == -1) { + perror("select failed"); + finish(EXIT_FAILURE); + } + + if (server.state == MSG_READ) { + if (!FD_ISSET(server.fd, &fdr)) { + return; + } + + // as a test, confirm with ioctl that we have data available + // after selecting + int available; + res = ioctl(server.fd, FIONREAD, &available); + assert(res != -1); + assert(available); + + res = do_msg_read(server.fd, &server.msg, echo_read, 0, NULL, NULL); + if (res != -1) echo_read += res; + + // once we've read the entire message, validate it + if (echo_read >= server.msg.length) { + assert(!strcmp(server.msg.buffer, MESSAGE)); + finish(EXIT_SUCCESS); + } + } else { + if (!FD_ISSET(server.fd, &fdw)) { + return; + } + + res = do_msg_write(server.fd, &echo_msg, echo_wrote, 0, NULL, 0); + if (res != -1) echo_wrote += res; + + // once we're done writing the message, read it back + if (echo_wrote >= echo_msg.length) { + server.state = MSG_READ; + } + } +} + +int main() { + struct sockaddr_in addr; + int res; + + memset(&server, 0, sizeof(server_t)); + server.state = MSG_WRITE; + + // setup the message we're going to echo + memset(&echo_msg, 0, sizeof(msg_t)); + echo_msg.length = strlen(MESSAGE) + 1; + echo_msg.buffer = malloc(echo_msg.length); + strncpy(echo_msg.buffer, MESSAGE, echo_msg.length); + + // create the socket and set to non-blocking +#if !USE_UDP + server.fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); +#else + server.fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); +#endif + if (server.fd == -1) { + perror("cannot create socket"); + finish(EXIT_FAILURE); + } + fcntl(server.fd, F_SETFL, O_NONBLOCK); + + // connect the socket + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(SOCKK); + if (inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr) != 1) { + perror("inet_pton failed"); + finish(EXIT_FAILURE); + } + + res = connect(server.fd, (struct sockaddr *)&addr, sizeof(addr)); + if (res == -1 && errno != EINPROGRESS) { + perror("connect failed"); + finish(EXIT_FAILURE); + } + +#if EMSCRIPTEN + emscripten_set_main_loop(main_loop, 0, 0); +#else + while (1) main_loop(NULL); +#endif + + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/tests/sockets/test_sockets_echo_server.c b/tests/sockets/test_sockets_echo_server.c new file mode 100644 index 0000000000000..8a48b8782174f --- /dev/null +++ b/tests/sockets/test_sockets_echo_server.c @@ -0,0 +1,152 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if EMSCRIPTEN +#include +#endif + +#include "test_sockets_msg.h" + +typedef enum { + MSG_READ, + MSG_WRITE +} msg_state_t; + +typedef struct { + int fd; +} server_t; + +typedef struct { + int fd; + struct sockaddr_in addr; + msg_t msg; + msg_state_t state; + int read; + int wrote; +} client_t; + +server_t server; +client_t client; + +void main_loop(void *arg) { + int res; + fd_set fdr; + fd_set fdw; + + // see if there are any connections to accept or read / write from + FD_ZERO(&fdr); + FD_ZERO(&fdw); + FD_SET(server.fd, &fdr); + FD_SET(server.fd, &fdw); +#if !USE_UDP + if (client.fd) FD_SET(client.fd, &fdr); + if (client.fd) FD_SET(client.fd, &fdw); +#endif + res = select(64, &fdr, &fdw, NULL, NULL); + if (res == -1) { + perror("select failed"); + exit(EXIT_SUCCESS); + } + +#if !USE_UDP + // for TCP sockets, we may need to accept a connection + if (FD_ISSET(server.fd, &fdr)) { + client.fd = accept(server.fd, NULL, NULL); + assert(client.fd != -1); + } +#endif + +#if !USE_UDP + int fd = client.fd; +#else + int fd = server.fd; +#endif + if (client.state == MSG_READ) { + socklen_t addrlen; + + if (!FD_ISSET(fd, &fdr)) { + return; + } + + res = do_msg_read(fd, &client.msg, client.read, 0, (struct sockaddr *)&client.addr, &addrlen); + if (res != -1) client.read += res; + + // once we've read the entire message, echo it back + if (client.read >= client.msg.length) { + client.read = 0; + client.state = MSG_WRITE; + } + } else { + if (!FD_ISSET(fd, &fdw)) { + return; + } + + res = do_msg_write(fd, &client.msg, client.wrote, 0, (struct sockaddr *)&client.addr, sizeof(client.addr)); + if (res != -1) client.wrote += res; + + // close the client once we've echo'd back the entire message + if (client.wrote >= client.msg.length) { + close(client.fd); + memset(&client, 0, sizeof(client_t)); + } + } +} + +int main() { + struct sockaddr_in addr; + int res; + + memset(&server, 0, sizeof(server_t)); + memset(&client, 0, sizeof(client_t)); + + // create the socket and set to non-blocking +#if !USE_UDP + server.fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); +#else + server.fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); +#endif + if (server.fd == -1) { + perror("cannot create socket"); + exit(EXIT_FAILURE); + } + fcntl(server.fd, F_SETFL, O_NONBLOCK); + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(SOCKK); + if (inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr) != 1) { + perror("inet_pton failed"); + exit(EXIT_FAILURE); + } + + res = bind(server.fd, (struct sockaddr *)&addr, sizeof(addr)); + if (res == -1) { + perror("bind failed"); + exit(EXIT_FAILURE); + } + +#if !USE_UDP + res = listen(server.fd, 50); + if (res == -1) { + perror("listen failed"); + exit(EXIT_FAILURE); + } +#endif + +#if EMSCRIPTEN + emscripten_set_main_loop(main_loop, 60, 0); +#else + while (1) main_loop(NULL); +#endif + + return EXIT_SUCCESS; +} diff --git a/tests/sockets/test_sockets_msg.h b/tests/sockets/test_sockets_msg.h new file mode 100644 index 0000000000000..30094d65fa149 --- /dev/null +++ b/tests/sockets/test_sockets_msg.h @@ -0,0 +1,78 @@ +#ifndef __TEST_SOCKETS_MSG_H__ +#define __TEST_SOCKETS_MSG_H__ + +typedef struct { + char *buffer; + int length; +} msg_t; + +int do_msg_read(int sockfd, msg_t *msg, int offset, int length, struct sockaddr *addr, socklen_t *addrlen) { + int res; + + if (!msg->length) { + // read the message length + res = recvfrom(sockfd, &msg->length, sizeof(int), 0, (struct sockaddr *)addr, addrlen); + if (res == -1) { + assert(errno == EAGAIN); + return res; + } + assert(res != 0); + msg->buffer = malloc(msg->length); + + printf("do_msg_read: allocating %d bytes for message\n", msg->length); + } + + // read the actual message + int max = msg->length - offset; + if (length && max > length) { + max = length; + } + res = recvfrom(sockfd, msg->buffer + offset, max, 0, (struct sockaddr *)addr, addrlen); + if (res == -1) { + assert(errno == EAGAIN); + return res; + } + + printf("do_msg_read: read %d bytes\n", res); + + return res; +} + +int do_msg_write(int sockfd, msg_t *msg, int offset, int length, struct sockaddr *addr, socklen_t addrlen) { + int res; + + // send the message length first + if (!offset) { + if (addr) { + res = sendto(sockfd, &msg->length, sizeof(int), 0, addr, addrlen); + } else { + res = send(sockfd, &msg->length, sizeof(int), 0); + } + if (res == -1) { + assert(errno == EAGAIN); + return res; + } + assert(res == sizeof(int)); + } + + // then the actual message + int max = msg->length - offset; + if (length && max > length) { + max = length; + } + if (addr) { + res = sendto(sockfd, msg->buffer + offset, max, 0, addr, addrlen); + } else { + res = send(sockfd, msg->buffer + offset, max, 0); + } + if (res == -1) { + assert(errno == EAGAIN); + return res; + } + + printf("do_msg_write: wrote %d bytes %d\n", res, msg->length); + + return res; +} + +#endif \ No newline at end of file diff --git a/tests/sockets/test_sockets_partial.c b/tests/sockets/test_sockets_partial_client.c similarity index 98% rename from tests/sockets/test_sockets_partial.c rename to tests/sockets/test_sockets_partial_client.c index 5fe3472195a39..dcf90f1996a6f 100644 --- a/tests/sockets/test_sockets_partial.c +++ b/tests/sockets/test_sockets_partial_client.c @@ -111,7 +111,7 @@ int main() { #if EMSCRIPTEN emscripten_set_main_loop(iter, 0, 0); #else - while (!done) iter(NULL); + while (1) iter(NULL); #endif return EXIT_SUCCESS; diff --git a/tests/sockets/test_sockets_partial_server.c b/tests/sockets/test_sockets_partial_server.c new file mode 100644 index 0000000000000..57fae84b9d3f9 --- /dev/null +++ b/tests/sockets/test_sockets_partial_server.c @@ -0,0 +1,130 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if EMSCRIPTEN +#include +#endif + +int serverfd = -1; +int clientfd = -1; + +// csock.send("\x09\x01\x02\x03\x04\x05\x06\x07\x08\x09") +// csock.send("\x08\x01\x02\x03\x04\x05\x06\x07\x08") +// csock.send("\x07\x01\x02\x03\x04\x05\x06\x07") +// csock.send("\x06\x01\x02\x03\x04\x05\x06") +// csock.send("\x05\x01\x02\x03\x04\x05") +// csock.send("\x04\x01\x02\x03\x04") +// csock.send("\x03\x01\x02\x03") +// csock.send("\x02\x01\x02") +// csock.send("\x01\x01") + +void do_send(int sockfd) { + static char* buffers[] = { + "\x09\x01\x02\x03\x04\x05\x06\x07\x08\x09\0", + "\x08\x01\x02\x03\x04\x05\x06\x07\x08\0", + "\x07\x01\x02\x03\x04\x05\x06\x07\0", + "\x06\x01\x02\x03\x04\x05\x06\0", + "\x05\x01\x02\x03\x04\x05\0", + "\x04\x01\x02\x03\x04\0", + "\x03\x01\x02\x03\0", + "\x02\x01\x02\0", + "\x01\x01\0" + }; + + int i; + int res; + char *buffer; + struct sockaddr_in addr; + socklen_t addrlen; + + for (i = 0; i < sizeof(buffers) / sizeof(char*); i++) { + buffer = buffers[i]; + + res = sendto(sockfd, buffer, strlen(buffer), 0, (struct sockaddr *)&addr, sizeof(addr)); + if (res == -1) { + perror("send failed"); + exit(EXIT_FAILURE); + } + printf("sent \"%s\" (%d bytes)\n", buffer, res); + } + + exit(EXIT_SUCCESS); +} + +void iter(void *arg) { + int res; + fd_set fdr; + fd_set fdw; + + // see if there are any connections to accept / write to + FD_ZERO(&fdr); + FD_ZERO(&fdw); + FD_SET(serverfd, &fdr); + if (clientfd != -1) FD_SET(clientfd, &fdw); + res = select(64, &fdr, &fdw, NULL, NULL); + if (res == -1) { + perror("select failed"); + exit(EXIT_SUCCESS); + } + + if (FD_ISSET(serverfd, &fdr)) { + printf("accepted someone\n"); + clientfd = accept(serverfd, NULL, NULL); + assert(clientfd != -1); + } + + if (FD_ISSET(clientfd, &fdw)) { + do_send(clientfd); + } +} + +int main() { + struct sockaddr_in addr; + int res; + + // create the socket + serverfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + if (serverfd == -1) { + perror("cannot create socket"); + exit(EXIT_FAILURE); + } + fcntl(serverfd, F_SETFL, O_NONBLOCK); + + // bind and listen to the supplied port + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(SOCKK); + if (inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr) != 1) { + perror("inet_pton failed"); + exit(EXIT_FAILURE); + } + + res = bind(serverfd, (struct sockaddr *)&addr, sizeof(addr)); + if (res == -1) { + perror("bind failed"); + exit(EXIT_FAILURE); + } + + res = listen(serverfd, 50); + if (res == -1) { + perror("listen failed"); + exit(EXIT_FAILURE); + } + +#if EMSCRIPTEN + emscripten_set_main_loop(iter, 60, 0); +#else + while (1) iter(NULL); +#endif + + return EXIT_SUCCESS; +} diff --git a/tests/sockets/test_sockets_select_server_closes_connection_rw.c b/tests/sockets/test_sockets_select_server_closes_connection_client_rw.c similarity index 84% rename from tests/sockets/test_sockets_select_server_closes_connection_rw.c rename to tests/sockets/test_sockets_select_server_closes_connection_client_rw.c index f7e19aca31145..198ad232f5da6 100644 --- a/tests/sockets/test_sockets_select_server_closes_connection_rw.c +++ b/tests/sockets/test_sockets_select_server_closes_connection_client_rw.c @@ -14,9 +14,13 @@ #include #endif -#define EXPECTED_BYTES 5 +#include "test_sockets_msg.h" -int sockfd = -1; +#define MESSAGE "0123456789" + +int sockfd; +msg_t readmsg; +msg_t writemsg; void finish(int result) { close(sockfd); @@ -26,12 +30,10 @@ void finish(int result) { exit(result); } -void iter(void *arg) { +void main_loop(void *arg) { static int state = 0; - static char writebuf[] = "01234567890123456789"; - static int writePos = 0; - static char readbuf[1024]; static int readPos = 0; + static int writePos = 0; int selectRes; ssize_t transferAmount; fd_set sett; @@ -63,11 +65,11 @@ void iter(void *arg) { } // send a single byte - transferAmount = send(sockfd, writebuf+writePos, 1, 0); - writePos += transferAmount; + transferAmount = do_msg_write(sockfd, &writemsg, writePos, 1, NULL, 0); + if (transferAmount != -1) writePos += transferAmount; // after 10 bytes switch to next state - if (writePos >= 10) { + if (writePos >= writemsg.length) { state = 1; } break; @@ -86,8 +88,8 @@ void iter(void *arg) { } // read a single byte - transferAmount = recv(sockfd, readbuf+readPos, 1, 0); - readPos += transferAmount; + transferAmount = do_msg_read(sockfd, &readmsg, readPos, 1, NULL, NULL); + if (transferAmount != -1) readPos += transferAmount; // if successfully reading 1 byte, switch to next state if (readPos >= 1) { @@ -119,11 +121,11 @@ void iter(void *arg) { } // read a single byte - transferAmount = recv(sockfd, readbuf+readPos, 1, 0); - readPos += transferAmount; + transferAmount = do_msg_read(sockfd, &readmsg, readPos, 1, NULL, NULL); + if (transferAmount != -1) readPos += transferAmount; // with 10 bytes read the inQueue is empty => switch state - if (readPos >= 10) { + if (readPos >= readmsg.length) { state = 3; } break; @@ -141,7 +143,7 @@ void iter(void *arg) { // but recv should return 0 signaling the remote // end has closed the connection. - transferAmount = recv(sockfd, readbuf, 1, 0); + transferAmount = do_msg_read(sockfd, &readmsg, readPos, 0, NULL, NULL); if (transferAmount) { printf("case 3: read != 0\n"); finish(EXIT_FAILURE); @@ -176,6 +178,12 @@ int main() { struct sockaddr_in addr; int res; + memset(&readmsg, 0, sizeof(msg_t)); + memset(&writemsg, 0, sizeof(msg_t)); + writemsg.length = strlen(MESSAGE) + 1; + writemsg.buffer = malloc(writemsg.length); + strncpy(writemsg.buffer, MESSAGE, writemsg.length); + sockfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); if (sockfd == -1) { perror("cannot create socket"); @@ -199,9 +207,9 @@ int main() { } #if EMSCRIPTEN - emscripten_set_main_loop(iter, 0, 0); + emscripten_set_main_loop(main_loop, 0, 0); #else - while (1) iter(NULL); + while (1) main_loop(NULL); #endif return EXIT_SUCCESS; diff --git a/tests/sockets/test_sockets_select.c b/tests/sockets/test_sockets_select_server_no_accept_client.c similarity index 100% rename from tests/sockets/test_sockets_select.c rename to tests/sockets/test_sockets_select_server_no_accept_client.c diff --git a/tests/sockets/test_sockets_select_server_no_accept_server.c b/tests/sockets/test_sockets_select_server_no_accept_server.c new file mode 100644 index 0000000000000..4a399ed1deb21 --- /dev/null +++ b/tests/sockets/test_sockets_select_server_no_accept_server.c @@ -0,0 +1,86 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if EMSCRIPTEN +#include +#endif + +int serverfd = -1; + +void iter(void *arg) { + int res; + fd_set fdr; + fd_set fdw; + + // see if there are any connections to accept / write to + FD_ZERO(&fdr); + FD_ZERO(&fdw); + FD_SET(serverfd, &fdr); + if (clientfd != -1) FD_SET(clientfd, &fdw); + res = select(64, &fdr, &fdw, NULL, NULL); + if (res == -1) { + perror("select failed"); + exit(EXIT_SUCCESS); + } + + if (FD_ISSET(serverfd, &fdr)) { + printf("accepted someone\n"); + clientfd = accept(serverfd, NULL, NULL); + assert(clientfd != -1); + } + + if (FD_ISSET(clientfd, &fdw)) { + do_send(clientfd); + } +} + +int main() { + struct sockaddr_in addr; + int res; + + // create the socket + serverfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + if (serverfd == -1) { + perror("cannot create socket"); + exit(EXIT_FAILURE); + } + fcntl(serverfd, F_SETFL, O_NONBLOCK); + + // bind and listen to the supplied port + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(SOCKK); + if (inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr) != 1) { + perror("inet_pton failed"); + exit(EXIT_FAILURE); + } + + res = bind(serverfd, (struct sockaddr *)&addr, sizeof(addr)); + if (res == -1) { + perror("bind failed"); + exit(EXIT_FAILURE); + } + + res = listen(serverfd, 50); + if (res == -1) { + perror("listen failed"); + exit(EXIT_FAILURE); + } + +#if EMSCRIPTEN + emscripten_set_main_loop(iter, 60, 0); +#else + while (1) iter(NULL); +#endif + + return EXIT_SUCCESS; +} From 35d58fd518da39db69adccd47882e2924961a861 Mon Sep 17 00:00:00 2001 From: Anthony Pesch Date: Fri, 9 Aug 2013 23:03:42 -0700 Subject: [PATCH 033/112] - merged FS and VFS namespaces - split off fs, memfs, sockfs and tty libraries --- src/library.js | 1781 +---------------------------------------- src/library_fs.js | 1367 +++++++++++++++++++++++++++++++ src/library_memfs.js | 217 +++++ src/library_sockfs.js | 18 + src/library_tty.js | 121 +++ src/modules.js | 2 +- 6 files changed, 1752 insertions(+), 1754 deletions(-) create mode 100644 src/library_fs.js create mode 100644 src/library_memfs.js create mode 100644 src/library_sockfs.js create mode 100644 src/library_tty.js diff --git a/src/library.js b/src/library.js index f0302aaa8f56f..4bc120b295319 100644 --- a/src/library.js +++ b/src/library.js @@ -18,1737 +18,12 @@ // Memory allocated during startup, in postsets, should only be ALLOC_STATIC LibraryManager.library = { - // ========================================================================== - // File system base. - // ========================================================================== - // keep this low in memory, because we flatten arrays with them in them stdin: 'allocate(1, "i32*", ALLOC_STATIC)', stdout: 'allocate(1, "i32*", ALLOC_STATIC)', stderr: 'allocate(1, "i32*", ALLOC_STATIC)', _impure_ptr: 'allocate(1, "i32*", ALLOC_STATIC)', - $FS__deps: ['$ERRNO_CODES', '$ERRNO_MESSAGES', '__setErrNo', '$VFS', '$PATH', '$TTY', '$MEMFS', 'stdin', 'stdout', 'stderr', 'fflush'], - $FS__postset: 'FS.staticInit();' + - '__ATINIT__.unshift({ func: function() { if (!Module["noFSInit"] && !FS.init.initialized) FS.init() } });' + - '__ATMAIN__.push({ func: function() { FS.ignorePermissions = false } });' + - '__ATEXIT__.push({ func: function() { FS.quit() } });' + - // export some names through closure - 'Module["FS_createFolder"] = FS.createFolder;' + - 'Module["FS_createPath"] = FS.createPath;' + - 'Module["FS_createDataFile"] = FS.createDataFile;' + - 'Module["FS_createPreloadedFile"] = FS.createPreloadedFile;' + - 'Module["FS_createLazyFile"] = FS.createLazyFile;' + - 'Module["FS_createLink"] = FS.createLink;' + - 'Module["FS_createDevice"] = FS.createDevice;', - $FS: { - root: null, - nodes: [null], - devices: [null], - streams: [null], - nextInode: 1, - name_table: new Array(4096), - currentPath: '/', - initialized: false, - // Whether we are currently ignoring permissions. Useful when preparing the - // filesystem and creating files inside read-only folders. - // This is set to false when the runtime is initialized, allowing you - // to modify the filesystem freely before run() is called. - ignorePermissions: true, - - ErrnoError: function(errno) { - this.errno = errno; - for (var key in ERRNO_CODES) { - if (ERRNO_CODES[key] === errno) { - this.code = key; - break; - } - } - this.message = ERRNO_MESSAGES[errno]; - }, - - handleFSError: function(e) { - if (!(e instanceof FS.ErrnoError)) throw e + ' : ' + new Error().stack; - return ___setErrNo(e.errno); - }, - - // - // nodes - // - hashName: function(parentid, name) { - var hash = 0; - for (var i = 0; i < name.length; i++) { - hash = ((hash << 5) - hash + name.charCodeAt(i)) | 0; - } - return (parentid + hash) % FS.name_table.length; - }, - hashAddNode: function(node) { - var hash = FS.hashName(node.parent.id, node.name); - node.name_next = FS.name_table[hash]; - FS.name_table[hash] = node; - }, - hashRemoveNode: function(node) { - var hash = FS.hashName(node.parent.id, node.name); - if (FS.name_table[hash] === node) { - FS.name_table[hash] = node.name_next; - } else { - var current = FS.name_table[hash]; - while (current) { - if (current.name_next === node) { - current.name_next = node.name_next; - break; - } - current = current.name_next; - } - } - }, - lookupNode: function(parent, name) { - var err = FS.mayLookup(parent); - if (err) { - throw new FS.ErrnoError(err); - } - var hash = FS.hashName(parent.id, name); - for (var node = FS.name_table[hash]; node; node = node.name_next) { - if (node.parent.id === parent.id && node.name === name) { - return node; - } - } - // if we failed to find it in the cache, call into the VFS - return VFS.lookup(parent, name); - }, - createNode: function(parent, name, mode, rdev) { - var node = { - id: FS.nextInode++, - name: name, - mode: mode, - node_ops: {}, - stream_ops: {}, - rdev: rdev, - parent: null, - mount: null - }; - if (!parent) { - parent = node; // root node sets parent to itself - } - node.parent = parent; - node.mount = parent.mount; - // compatibility - var readMode = {{{ cDefine('S_IRUGO') }}} | {{{ cDefine('S_IXUGO') }}}; - var writeMode = {{{ cDefine('S_IWUGO') }}}; - // NOTE we must use Object.defineProperties instead of individual calls to - // Object.defineProperty in order to make closure compiler happy - Object.defineProperties(node, { - read: { - get: function() { return (node.mode & readMode) === readMode; }, - set: function(val) { val ? node.mode |= readMode : node.mode &= ~readMode; } - }, - write: { - get: function() { return (node.mode & writeMode) === writeMode; }, - set: function(val) { val ? node.mode |= writeMode : node.mode &= ~writeMode; } - }, - isFolder: { - get: function() { return FS.isDir(node.mode); }, - }, - isDevice: { - get: function() { return FS.isChrdev(node.mode); }, - }, - }); - FS.hashAddNode(node); - return node; - }, - destroyNode: function(node) { - FS.hashRemoveNode(node); - }, - isRoot: function(node) { - return node === node.parent; - }, - isMountpoint: function(node) { - return node.mounted; - }, - isFile: function(mode) { - return (mode & {{{ cDefine('S_IFMT') }}}) === {{{ cDefine('S_IFREG') }}}; - }, - isDir: function(mode) { - return (mode & {{{ cDefine('S_IFMT') }}}) === {{{ cDefine('S_IFDIR') }}}; - }, - isLink: function(mode) { - return (mode & {{{ cDefine('S_IFMT') }}}) === {{{ cDefine('S_IFLNK') }}}; - }, - isChrdev: function(mode) { - return (mode & {{{ cDefine('S_IFMT') }}}) === {{{ cDefine('S_IFCHR') }}}; - }, - isBlkdev: function(mode) { - return (mode & {{{ cDefine('S_IFMT') }}}) === {{{ cDefine('S_IFBLK') }}}; - }, - isFIFO: function(mode) { - return (mode & {{{ cDefine('S_IFMT') }}}) === {{{ cDefine('S_IFIFO') }}}; - }, - - // - // paths - // - cwd: function() { - return FS.currentPath; - }, - lookupPath: function(path, opts) { - path = PATH.resolve(FS.currentPath, path); - opts = opts || { recurse_count: 0 }; - - if (opts.recurse_count > 8) { // max recursive lookup of 8 - throw new FS.ErrnoError(ERRNO_CODES.ELOOP); - } - - // split the path - var parts = PATH.normalizeArray(path.split('/').filter(function(p) { - return !!p; - }), false); - - // start at the root - var current = FS.root; - var current_path = '/'; - - for (var i = 0; i < parts.length; i++) { - var islast = (i === parts.length-1); - if (islast && opts.parent) { - // stop resolving - break; - } - - current = FS.lookupNode(current, parts[i]); - current_path = PATH.join(current_path, parts[i]); - - // jump to the mount's root node if this is a mountpoint - if (FS.isMountpoint(current)) { - current = current.mount.root; - } - - // follow symlinks - // by default, lookupPath will not follow a symlink if it is the final path component. - // setting opts.follow = true will override this behavior. - if (!islast || opts.follow) { - var count = 0; - while (FS.isLink(current.mode)) { - var link = VFS.readlink(current_path); - current_path = PATH.resolve(PATH.dirname(current_path), link); - - var lookup = FS.lookupPath(current_path, { recurse_count: opts.recurse_count }); - current = lookup.node; - - if (count++ > 40) { // limit max consecutive symlinks to 40 (SYMLOOP_MAX). - throw new FS.ErrnoError(ERRNO_CODES.ELOOP); - } - } - } - } - - return { path: current_path, node: current }; - }, - getPath: function(node) { - var path; - while (true) { - if (FS.isRoot(node)) { - return path ? PATH.join(node.mount.mountpoint, path) : node.mount.mountpoint; - } - path = path ? PATH.join(node.name, path) : node.name; - node = node.parent; - } - }, - - // - // permissions - // - flagModes: { - '"r"': {{{ cDefine('O_RDONLY') }}}, - '"rs"': {{{ cDefine('O_RDONLY') }}} | {{{ cDefine('O_SYNC') }}}, - '"r+"': {{{ cDefine('O_RDWR') }}}, - '"w"': {{{ cDefine('O_TRUNC') }}} | {{{ cDefine('O_CREAT') }}} | {{{ cDefine('O_WRONLY') }}}, - '"wx"': {{{ cDefine('O_TRUNC') }}} | {{{ cDefine('O_CREAT') }}} | {{{ cDefine('O_WRONLY') }}} | {{{ cDefine('O_EXCL') }}}, - '"xw"': {{{ cDefine('O_TRUNC') }}} | {{{ cDefine('O_CREAT') }}} | {{{ cDefine('O_WRONLY') }}} | {{{ cDefine('O_EXCL') }}}, - '"w+"': {{{ cDefine('O_TRUNC') }}} | {{{ cDefine('O_CREAT') }}} | {{{ cDefine('O_RDWR') }}}, - '"wx+"': {{{ cDefine('O_TRUNC') }}} | {{{ cDefine('O_CREAT') }}} | {{{ cDefine('O_RDWR') }}} | {{{ cDefine('O_EXCL') }}}, - '"xw+"': {{{ cDefine('O_TRUNC') }}} | {{{ cDefine('O_CREAT') }}} | {{{ cDefine('O_RDWR') }}} | {{{ cDefine('O_EXCL') }}}, - '"a"': {{{ cDefine('O_APPEND') }}} | {{{ cDefine('O_CREAT') }}} | {{{ cDefine('O_WRONLY') }}}, - '"ax"': {{{ cDefine('O_APPEND') }}} | {{{ cDefine('O_CREAT') }}} | {{{ cDefine('O_WRONLY') }}} | {{{ cDefine('O_EXCL') }}}, - '"xa"': {{{ cDefine('O_APPEND') }}} | {{{ cDefine('O_CREAT') }}} | {{{ cDefine('O_WRONLY') }}} | {{{ cDefine('O_EXCL') }}}, - '"a+"': {{{ cDefine('O_APPEND') }}} | {{{ cDefine('O_CREAT') }}} | {{{ cDefine('O_RDWR') }}}, - '"ax+"': {{{ cDefine('O_APPEND') }}} | {{{ cDefine('O_CREAT') }}} | {{{ cDefine('O_RDWR') }}} | {{{ cDefine('O_EXCL') }}}, - '"xa+"': {{{ cDefine('O_APPEND') }}} | {{{ cDefine('O_CREAT') }}} | {{{ cDefine('O_RDWR') }}} | {{{ cDefine('O_EXCL') }}} - }, - // convert the 'r', 'r+', etc. to it's corresponding set of O_* flags - modeStringToFlags: function(str) { - var flags = FS.flagModes[str]; - if (typeof flags === 'undefined') { - throw new Error('Unknown file open mode: ' + str); - } - return flags; - }, - // convert O_* bitmask to a string for nodePermissions - flagsToPermissionString: function(flag) { - var accmode = flag & {{{ cDefine('O_ACCMODE') }}}; - var perms = ['r', 'w', 'rw'][accmode]; - if ((flag & {{{ cDefine('O_TRUNC') }}})) { - perms += 'w'; - } - return perms; - }, - nodePermissions: function(node, perms) { - if (FS.ignorePermissions) { - return 0; - } - // return 0 if any user, group or owner bits are set. - if (perms.indexOf('r') !== -1 && !(node.mode & {{{ cDefine('S_IRUGO') }}})) { - return ERRNO_CODES.EACCES; - } else if (perms.indexOf('w') !== -1 && !(node.mode & {{{ cDefine('S_IWUGO') }}})) { - return ERRNO_CODES.EACCES; - } else if (perms.indexOf('x') !== -1 && !(node.mode & {{{ cDefine('S_IXUGO') }}})) { - return ERRNO_CODES.EACCES; - } - return 0; - }, - mayLookup: function(dir) { - return FS.nodePermissions(dir, 'x'); - }, - mayMknod: function(mode) { - switch (mode & {{{ cDefine('S_IFMT') }}}) { - case {{{ cDefine('S_IFREG') }}}: - case {{{ cDefine('S_IFCHR') }}}: - case {{{ cDefine('S_IFBLK') }}}: - case {{{ cDefine('S_IFIFO') }}}: - case {{{ cDefine('S_IFSOCK') }}}: - return 0; - default: - return ERRNO_CODES.EINVAL; - } - }, - mayCreate: function(dir, name) { - try { - var node = FS.lookupNode(dir, name); - return ERRNO_CODES.EEXIST; - } catch (e) { - } - return FS.nodePermissions(dir, 'wx'); - }, - mayDelete: function(dir, name, isdir) { - var node; - try { - node = FS.lookupNode(dir, name); - } catch (e) { - return e.errno; - } - var err = FS.nodePermissions(dir, 'wx'); - if (err) { - return err; - } - if (isdir) { - if (!FS.isDir(node.mode)) { - return ERRNO_CODES.ENOTDIR; - } - if (FS.isRoot(node) || FS.getPath(node) === FS.currentPath) { - return ERRNO_CODES.EBUSY; - } - } else { - if (FS.isDir(node.mode)) { - return ERRNO_CODES.EISDIR; - } - } - return 0; - }, - mayOpen: function(node, flags) { - if (!node) { - return ERRNO_CODES.ENOENT; - } - if (FS.isLink(node.mode)) { - return ERRNO_CODES.ELOOP; - } else if (FS.isDir(node.mode)) { - if ((flags & {{{ cDefine('O_ACCMODE') }}}) !== {{{ cDefine('O_RDONLY')}}} || // opening for write - (flags & {{{ cDefine('O_TRUNC') }}})) { - return ERRNO_CODES.EISDIR; - } - } - return FS.nodePermissions(node, FS.flagsToPermissionString(flags)); - }, - - // - // devices - // - // each character device consists of a device id + stream operations. - // when a character device node is created (e.g. /dev/stdin) it is - // assigned a device id that lets us map back to the actual device. - // by default, each character device stream (e.g. _stdin) uses chrdev_stream_ops. - // however, once opened, the stream's operations are overridden with - // the operations of the device its underlying node maps back to. - chrdev_stream_ops: { - open: function(stream) { - var device = FS.getDevice(stream.node.rdev); - // override node's stream ops with the device's - stream.stream_ops = device.stream_ops; - // forward the open call - if (stream.stream_ops.open) { - stream.stream_ops.open(stream); - } - }, - llseek: function() { - throw new FS.ErrnoError(ERRNO_CODES.ESPIPE); - } - }, - major: function(dev) { - return ((dev) >> 8); - }, - minor: function(dev) { - return ((dev) & 0xff); - }, - makedev: function(ma, mi) { - return ((ma) << 8 | (mi)); - }, - registerDevice: function(dev, ops) { - FS.devices[dev] = { stream_ops: ops }; - }, - getDevice: function(dev) { - return FS.devices[dev]; - }, - - // - // streams - // - MAX_OPEN_FDS: 4096, - nextfd: function(fd_start, fd_end) { - fd_start = fd_start || 1; - fd_end = fd_end || FS.MAX_OPEN_FDS; - for (var fd = fd_start; fd <= fd_end; fd++) { - if (!FS.streams[fd]) { - return fd; - } - } - throw new FS.ErrnoError(ERRNO_CODES.EMFILE); - }, - getStream: function(fd) { - return FS.streams[fd]; - }, - createStream: function(stream, fd_start, fd_end) { - var fd = FS.nextfd(fd_start, fd_end); - stream.fd = fd; - // compatibility - Object.defineProperties(stream, { - object: { - get: function() { return stream.node; }, - set: function(val) { stream.node = val; } - }, - isRead: { - get: function() { return (stream.flags & {{{ cDefine('O_ACCMODE') }}}) !== {{{ cDefine('O_WRONLY') }}}; } - }, - isWrite: { - get: function() { return (stream.flags & {{{ cDefine('O_ACCMODE') }}}) !== {{{ cDefine('O_RDONLY') }}}; } - }, - isAppend: { - get: function() { return (stream.flags & {{{ cDefine('O_APPEND') }}}); } - } - }); - FS.streams[fd] = stream; - return stream; - }, - closeStream: function(fd) { - FS.streams[fd] = null; - }, - - // - // general - // - createDefaultDirectories: function() { - VFS.mkdir('/tmp', 0777); - }, - createDefaultDevices: function() { - // create /dev - VFS.mkdir('/dev', 0777); - // setup /dev/null - FS.registerDevice(FS.makedev(1, 3), { - read: function() { return 0; }, - write: function() { return 0; } - }); - VFS.mkdev('/dev/null', 0666, FS.makedev(1, 3)); - // setup /dev/tty and /dev/tty1 - // stderr needs to print output using Module['printErr'] - // so we register a second tty just for it. - TTY.register(FS.makedev(5, 0), TTY.default_tty_ops); - TTY.register(FS.makedev(6, 0), TTY.default_tty1_ops); - VFS.mkdev('/dev/tty', 0666, FS.makedev(5, 0)); - VFS.mkdev('/dev/tty1', 0666, FS.makedev(6, 0)); - // we're not going to emulate the actual shm device, - // just create the tmp dirs that reside in it commonly - VFS.mkdir('/dev/shm', 0777); - VFS.mkdir('/dev/shm/tmp', 0777); - }, - createStandardStreams: function() { - // TODO deprecate the old functionality of a single - // input / output callback and that utilizes FS.createDevice - // and instead require a unique set of stream ops - - // by default, we symlink the standard streams to the - // default tty devices. however, if the standard streams - // have been overwritten we create a unique device for - // them instead. - if (Module['stdin']) { - FS.createDevice('/dev', 'stdin', Module['stdin']); - } else { - VFS.symlink('/dev/tty', '/dev/stdin'); - } - if (Module['stdout']) { - FS.createDevice('/dev', 'stdout', null, Module['stdout']); - } else { - VFS.symlink('/dev/tty', '/dev/stdout'); - } - if (Module['stderr']) { - FS.createDevice('/dev', 'stderr', null, Module['stderr']); - } else { - VFS.symlink('/dev/tty1', '/dev/stderr'); - } - - // open default streams for the stdin, stdout and stderr devices - var stdin = VFS.open('/dev/stdin', 'r'); - {{{ makeSetValue(makeGlobalUse('_stdin'), 0, 'stdin.fd', 'void*') }}}; - assert(stdin.fd === 1, 'invalid handle for stdin (' + stdin.fd + ')'); - - var stdout = VFS.open('/dev/stdout', 'w'); - {{{ makeSetValue(makeGlobalUse('_stdout'), 0, 'stdout.fd', 'void*') }}}; - assert(stdout.fd === 2, 'invalid handle for stdout (' + stdout.fd + ')'); - - var stderr = VFS.open('/dev/stderr', 'w'); - {{{ makeSetValue(makeGlobalUse('_stderr'), 0, 'stderr.fd', 'void*') }}}; - assert(stderr.fd === 3, 'invalid handle for stderr (' + stderr.fd + ')'); - }, - staticInit: function() { - FS.root = FS.createNode(null, '/', {{{ cDefine('S_IFDIR') }}} | 0777, 0); - VFS.mount(MEMFS, {}, '/'); - - FS.createDefaultDirectories(); - FS.createDefaultDevices(); - }, - init: function(input, output, error) { - assert(!FS.init.initialized, 'FS.init was previously called. If you want to initialize later with custom parameters, remove any earlier calls (note that one is automatically added to the generated code)'); - FS.init.initialized = true; - - // Allow Module.stdin etc. to provide defaults, if none explicitly passed to us here - Module['stdin'] = input || Module['stdin']; - Module['stdout'] = output || Module['stdout']; - Module['stderr'] = error || Module['stderr']; - - FS.createStandardStreams(); - }, - quit: function() { - FS.init.initialized = false; - for (var i = 0; i < FS.streams.length; i++) { - var stream = FS.streams[i]; - if (!stream) { - continue; - } - VFS.close(stream); - } - }, - - // - // compatibility - // - getMode: function(canRead, canWrite) { - var mode = 0; - if (canRead) mode |= {{{ cDefine('S_IRUGO') }}} | {{{ cDefine('S_IXUGO') }}}; - if (canWrite) mode |= {{{ cDefine('S_IWUGO') }}}; - return mode; - }, - joinPath: function(parts, forceRelative) { - var path = PATH.join.apply(null, parts); - if (forceRelative && path[0] == '/') path = path.substr(1); - return path; - }, - absolutePath: function(relative, base) { - return PATH.resolve(base, relative); - }, - standardizePath: function(path) { - return PATH.normalize(path); - }, - findObject: function(path, dontResolveLastLink) { - var ret = FS.analyzePath(path, dontResolveLastLink); - if (ret.exists) { - return ret.object; - } else { - ___setErrNo(ret.error); - return null; - } - }, - analyzePath: function(path, dontResolveLastLink) { - // operate from within the context of the symlink's target - try { - var lookup = FS.lookupPath(path, { follow: !dontResolveLastLink }); - path = lookup.path; - } catch (e) { - } - var ret = { - isRoot: false, exists: false, error: 0, name: null, path: null, object: null, - parentExists: false, parentPath: null, parentObject: null - }; - try { - var lookup = FS.lookupPath(path, { parent: true }); - ret.parentExists = true; - ret.parentPath = lookup.path; - ret.parentObject = lookup.node; - ret.name = PATH.basename(path); - lookup = FS.lookupPath(path, { follow: !dontResolveLastLink }); - ret.exists = true; - ret.path = lookup.path; - ret.object = lookup.node; - ret.name = lookup.node.name; - ret.isRoot = lookup.path === '/'; - } catch (e) { - ret.error = e.errno; - }; - return ret; - }, - createFolder: function(parent, name, canRead, canWrite) { - var path = PATH.join(typeof parent === 'string' ? parent : FS.getPath(parent), name); - var mode = FS.getMode(canRead, canWrite); - return VFS.mkdir(path, mode); - }, - createPath: function(parent, path, canRead, canWrite) { - parent = typeof parent === 'string' ? parent : FS.getPath(parent); - var parts = path.split('/').reverse(); - while (parts.length) { - var part = parts.pop(); - if (!part) continue; - var current = PATH.join(parent, part); - try { - VFS.mkdir(current, 0777); - } catch (e) { - // ignore EEXIST - } - parent = current; - } - return current; - }, - createFile: function(parent, name, properties, canRead, canWrite) { - var path = PATH.join(typeof parent === 'string' ? parent : FS.getPath(parent), name); - var mode = FS.getMode(canRead, canWrite); - return VFS.create(path, mode); - }, - createDataFile: function(parent, name, data, canRead, canWrite) { - var path = PATH.join(typeof parent === 'string' ? parent : FS.getPath(parent), name); - var mode = FS.getMode(canRead, canWrite); - var node = VFS.create(path, mode); - if (data) { - if (typeof data === 'string') { - var arr = new Array(data.length); - for (var i = 0, len = data.length; i < len; ++i) arr[i] = data.charCodeAt(i); - data = arr; - } - // make sure we can write to the file - VFS.chmod(path, mode | {{{ cDefine('S_IWUGO') }}}); - var stream = VFS.open(path, 'w'); - VFS.write(stream, data, 0, data.length, 0); - VFS.close(stream); - VFS.chmod(path, mode); - } - return node; - }, - createDevice: function(parent, name, input, output) { - var path = PATH.join(typeof parent === 'string' ? parent : FS.getPath(parent), name); - var mode = input && output ? 0777 : (input ? 0333 : 0555); - if (!FS.createDevice.major) FS.createDevice.major = 64; - var dev = FS.makedev(FS.createDevice.major++, 0); - // Create a fake device that a set of stream ops to emulate - // the old behavior. - FS.registerDevice(dev, { - open: function(stream) { - stream.seekable = false; - }, - close: function(stream) { - // flush any pending line data - if (output && output.buffer && output.buffer.length) { - output({{{ charCode('\n') }}}); - } - }, - read: function(stream, buffer, offset, length, pos /* ignored */) { - var bytesRead = 0; - for (var i = 0; i < length; i++) { - var result; - try { - result = input(); - } catch (e) { - throw new FS.ErrnoError(ERRNO_CODES.EIO); - } - if (result === undefined && bytesRead === 0) { - throw new FS.ErrnoError(ERRNO_CODES.EAGAIN); - } - if (result === null || result === undefined) break; - bytesRead++; - buffer[offset+i] = result; - } - if (bytesRead) { - stream.node.timestamp = Date.now(); - } - return bytesRead; - }, - write: function(stream, buffer, offset, length, pos) { - for (var i = 0; i < length; i++) { - try { - output(buffer[offset+i]); - } catch (e) { - throw new FS.ErrnoError(ERRNO_CODES.EIO); - } - } - if (length) { - stream.node.timestamp = Date.now(); - } - return i; - } - }); - return VFS.mkdev(path, mode, dev); - }, - createLink: function(parent, name, target, canRead, canWrite) { - var path = PATH.join(typeof parent === 'string' ? parent : FS.getPath(parent), name); - return VFS.symlink(target, path); - }, - // Makes sure a file's contents are loaded. Returns whether the file has - // been loaded successfully. No-op for files that have been loaded already. - forceLoadFile: function(obj) { - if (obj.isDevice || obj.isFolder || obj.link || obj.contents) return true; - var success = true; - if (typeof XMLHttpRequest !== 'undefined') { - throw new Error("Lazy loading should have been performed (contents set) in createLazyFile, but it was not. Lazy loading only works in web workers. Use --embed-file or --preload-file in emcc on the main thread."); - } else if (Module['read']) { - // Command-line. - try { - // WARNING: Can't read binary files in V8's d8 or tracemonkey's js, as - // read() will try to parse UTF8. - obj.contents = intArrayFromString(Module['read'](obj.url), true); - } catch (e) { - success = false; - } - } else { - throw new Error('Cannot load without read() or XMLHttpRequest.'); - } - if (!success) ___setErrNo(ERRNO_CODES.EIO); - return success; - }, - // Creates a file record for lazy-loading from a URL. XXX This requires a synchronous - // XHR, which is not possible in browsers except in a web worker! Use preloading, - // either --preload-file in emcc or FS.createPreloadedFile - createLazyFile: function(parent, name, url, canRead, canWrite) { - if (typeof XMLHttpRequest !== 'undefined') { - if (!ENVIRONMENT_IS_WORKER) throw 'Cannot do synchronous binary XHRs outside webworkers in modern browsers. Use --embed-file or --preload-file in emcc'; - // Lazy chunked Uint8Array (implements get and length from Uint8Array). Actual getting is abstracted away for eventual reuse. - var LazyUint8Array = function() { - this.lengthKnown = false; - this.chunks = []; // Loaded chunks. Index is the chunk number - } - LazyUint8Array.prototype.get = function(idx) { - if (idx > this.length-1 || idx < 0) { - return undefined; - } - var chunkOffset = idx % this.chunkSize; - var chunkNum = Math.floor(idx / this.chunkSize); - return this.getter(chunkNum)[chunkOffset]; - } - LazyUint8Array.prototype.setDataGetter = function(getter) { - this.getter = getter; - } - LazyUint8Array.prototype.cacheLength = function() { - // Find length - var xhr = new XMLHttpRequest(); - xhr.open('HEAD', url, false); - xhr.send(null); - if (!(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304)) throw new Error("Couldn't load " + url + ". Status: " + xhr.status); - var datalength = Number(xhr.getResponseHeader("Content-length")); - var header; - var hasByteServing = (header = xhr.getResponseHeader("Accept-Ranges")) && header === "bytes"; -#if SMALL_XHR_CHUNKS - var chunkSize = 1024; // Chunk size in bytes -#else - var chunkSize = 1024*1024; // Chunk size in bytes -#endif - - if (!hasByteServing) chunkSize = datalength; - - // Function to get a range from the remote URL. - var doXHR = (function(from, to) { - if (from > to) throw new Error("invalid range (" + from + ", " + to + ") or no bytes requested!"); - if (to > datalength-1) throw new Error("only " + datalength + " bytes available! programmer error!"); - - // TODO: Use mozResponseArrayBuffer, responseStream, etc. if available. - var xhr = new XMLHttpRequest(); - xhr.open('GET', url, false); - if (datalength !== chunkSize) xhr.setRequestHeader("Range", "bytes=" + from + "-" + to); - - // Some hints to the browser that we want binary data. - if (typeof Uint8Array != 'undefined') xhr.responseType = 'arraybuffer'; - if (xhr.overrideMimeType) { - xhr.overrideMimeType('text/plain; charset=x-user-defined'); - } - - xhr.send(null); - if (!(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304)) throw new Error("Couldn't load " + url + ". Status: " + xhr.status); - if (xhr.response !== undefined) { - return new Uint8Array(xhr.response || []); - } else { - return intArrayFromString(xhr.responseText || '', true); - } - }); - var lazyArray = this; - lazyArray.setDataGetter(function(chunkNum) { - var start = chunkNum * chunkSize; - var end = (chunkNum+1) * chunkSize - 1; // including this byte - end = Math.min(end, datalength-1); // if datalength-1 is selected, this is the last block - if (typeof(lazyArray.chunks[chunkNum]) === "undefined") { - lazyArray.chunks[chunkNum] = doXHR(start, end); - } - if (typeof(lazyArray.chunks[chunkNum]) === "undefined") throw new Error("doXHR failed!"); - return lazyArray.chunks[chunkNum]; - }); - - this._length = datalength; - this._chunkSize = chunkSize; - this.lengthKnown = true; - } - - var lazyArray = new LazyUint8Array(); - Object.defineProperty(lazyArray, "length", { - get: function() { - if(!this.lengthKnown) { - this.cacheLength(); - } - return this._length; - } - }); - Object.defineProperty(lazyArray, "chunkSize", { - get: function() { - if(!this.lengthKnown) { - this.cacheLength(); - } - return this._chunkSize; - } - }); - - var properties = { isDevice: false, contents: lazyArray }; - } else { - var properties = { isDevice: false, url: url }; - } - - var node = FS.createFile(parent, name, properties, canRead, canWrite); - // This is a total hack, but I want to get this lazy file code out of the - // core of MEMFS. If we want to keep this lazy file concept I feel it should - // be its own thin LAZYFS proxying calls to MEMFS. - if (properties.contents) { - node.contents = properties.contents; - } else if (properties.url) { - node.contents = null; - node.url = properties.url; - } - // override each stream op with one that tries to force load the lazy file first - var stream_ops = {}; - var keys = Object.keys(node.stream_ops); - keys.forEach(function(key) { - var fn = node.stream_ops[key]; - stream_ops[key] = function() { - if (!FS.forceLoadFile(node)) { - throw new FS.ErrnoError(ERRNO_CODES.EIO); - } - return fn.apply(null, arguments); - }; - }); - // use a custom read function - stream_ops.read = function(stream, buffer, offset, length, position) { - var contents = stream.node.contents; - var size = Math.min(contents.length - position, length); - if (contents.slice) { // normal array - for (var i = 0; i < size; i++) { - buffer[offset + i] = contents[position + i]; - } - } else { - for (var i = 0; i < size; i++) { // LazyUint8Array from sync binary XHR - buffer[offset + i] = contents.get(position + i); - } - } - return size; - }; - node.stream_ops = stream_ops; - return node; - }, - // Preloads a file asynchronously. You can call this before run, for example in - // preRun. run will be delayed until this file arrives and is set up. - // If you call it after run(), you may want to pause the main loop until it - // completes, if so, you can use the onload parameter to be notified when - // that happens. - // In addition to normally creating the file, we also asynchronously preload - // the browser-friendly versions of it: For an image, we preload an Image - // element and for an audio, and Audio. These are necessary for SDL_Image - // and _Mixer to find the files in preloadedImages/Audios. - // You can also call this with a typed array instead of a url. It will then - // do preloading for the Image/Audio part, as if the typed array were the - // result of an XHR that you did manually. - createPreloadedFile: function(parent, name, url, canRead, canWrite, onload, onerror, dontCreateFile) { - Browser.init(); - // TODO we should allow people to just pass in a complete filename instead - // of parent and name being that we just join them anyways - var fullname = PATH.resolve(PATH.join(parent, name)); - function processData(byteArray) { - function finish(byteArray) { - if (!dontCreateFile) { - FS.createDataFile(parent, name, byteArray, canRead, canWrite); - } - if (onload) onload(); - removeRunDependency('cp ' + fullname); - } - var handled = false; - Module['preloadPlugins'].forEach(function(plugin) { - if (handled) return; - if (plugin['canHandle'](fullname)) { - plugin['handle'](byteArray, fullname, finish, function() { - if (onerror) onerror(); - removeRunDependency('cp ' + fullname); - }); - handled = true; - } - }); - if (!handled) finish(byteArray); - } - addRunDependency('cp ' + fullname); - if (typeof url == 'string') { - Browser.asyncLoad(url, function(byteArray) { - processData(byteArray); - }, onerror); - } else { - processData(url); - } - } - }, - - $VFS__deps: ['$FS'], - $VFS: { - mount: function(type, opts, mountpoint) { - var mount = { - type: type, - opts: opts, - mountpoint: mountpoint, - root: null - }; - var lookup; - if (mountpoint) { - lookup = FS.lookupPath(mountpoint, { follow: false }); - } - // create a root node for the fs - var root = type.mount(mount); - root.mount = mount; - mount.root = root; - // assign the mount info to the mountpoint's node - if (lookup) { - lookup.node.mount = mount; - lookup.node.mounted = true; - // compatibility update FS.root if we mount to / - if (mountpoint === '/') { - FS.root = mount.root; - } - } - return root; - }, - lookup: function(parent, name) { - return parent.node_ops.lookup(parent, name); - }, - // generic function for all node creation - mknod: function(path, mode, dev) { - var lookup = FS.lookupPath(path, { parent: true }); - var parent = lookup.node; - var name = PATH.basename(path); - var err = FS.mayCreate(parent, name); - if (err) { - throw new FS.ErrnoError(err); - } - if (!parent.node_ops.mknod) { - throw new FS.ErrnoError(ERRNO_CODES.EPERM); - } - return parent.node_ops.mknod(parent, name, mode, dev); - }, - // helpers to create specific types of nodes - create: function(path, mode) { - mode &= {{{ cDefine('S_IALLUGO') }}}; - mode |= {{{ cDefine('S_IFREG') }}}; - return VFS.mknod(path, mode, 0); - }, - mkdir: function(path, mode) { - mode &= {{{ cDefine('S_IRWXUGO') }}} | {{{ cDefine('S_ISVTX') }}}; - mode |= {{{ cDefine('S_IFDIR') }}}; - return VFS.mknod(path, mode, 0); - }, - mkdev: function(path, mode, dev) { - mode |= {{{ cDefine('S_IFCHR') }}}; - return VFS.mknod(path, mode, dev); - }, - symlink: function(oldpath, newpath) { - var lookup = FS.lookupPath(newpath, { parent: true }); - var parent = lookup.node; - var newname = PATH.basename(newpath); - var err = FS.mayCreate(parent, newname); - if (err) { - throw new FS.ErrnoError(err); - } - if (!parent.node_ops.symlink) { - throw new FS.ErrnoError(ERRNO_CODES.EPERM); - } - return parent.node_ops.symlink(parent, newname, oldpath); - }, - rename: function(old_path, new_path) { - var old_dirname = PATH.dirname(old_path); - var new_dirname = PATH.dirname(new_path); - var old_name = PATH.basename(old_path); - var new_name = PATH.basename(new_path); - // parents must exist - var lookup, old_dir, new_dir; - try { - lookup = FS.lookupPath(old_path, { parent: true }); - old_dir = lookup.node; - lookup = FS.lookupPath(new_path, { parent: true }); - new_dir = lookup.node; - } catch (e) { - throw new FS.ErrnoError(ERRNO_CODES.EBUSY); - } - // need to be part of the same mount - if (old_dir.mount !== new_dir.mount) { - throw new FS.ErrnoError(ERRNO_CODES.EXDEV); - } - // source must exist - var old_node = FS.lookupNode(old_dir, old_name); - // old path should not be an ancestor of the new path - var relative = PATH.relative(old_path, new_dirname); - if (relative.charAt(0) !== '.') { - throw new FS.ErrnoError(ERRNO_CODES.EINVAL); - } - // new path should not be an ancestor of the old path - relative = PATH.relative(new_path, old_dirname); - if (relative.charAt(0) !== '.') { - throw new FS.ErrnoError(ERRNO_CODES.ENOTEMPTY); - } - // see if the new path already exists - var new_node; - try { - new_node = FS.lookupNode(new_dir, new_name); - } catch (e) { - // not fatal - } - // early out if nothing needs to change - if (old_node === new_node) { - return; - } - // we'll need to delete the old entry - var isdir = FS.isDir(old_node.mode); - var err = FS.mayDelete(old_dir, old_name, isdir); - if (err) { - throw new FS.ErrnoError(err); - } - // need delete permissions if we'll be overwriting. - // need create permissions if new doesn't already exist. - err = new_node ? - FS.mayDelete(new_dir, new_name, isdir) : - FS.mayCreate(new_dir, new_name); - if (err) { - throw new FS.ErrnoError(err); - } - if (!old_dir.node_ops.rename) { - throw new FS.ErrnoError(ERRNO_CODES.EPERM); - } - if (FS.isMountpoint(old_node) || (new_node && FS.isMountpoint(new_node))) { - throw new FS.ErrnoError(ERRNO_CODES.EBUSY); - } - // if we are going to change the parent, check write permissions - if (new_dir !== old_dir) { - err = FS.nodePermissions(old_dir, 'w'); - if (err) { - throw new FS.ErrnoError(err); - } - } - // remove the node from the lookup hash - FS.hashRemoveNode(old_node); - // do the underlying fs rename - try { - old_node.node_ops.rename(old_node, new_dir, new_name); - } catch (e) { - throw e; - } finally { - // add the node back to the hash (in case node_ops.rename - // changed its name) - FS.hashAddNode(old_node); - } - }, - rmdir: function(path) { - var lookup = FS.lookupPath(path, { parent: true }); - var parent = lookup.node; - var name = PATH.basename(path); - var node = FS.lookupNode(parent, name); - var err = FS.mayDelete(parent, name, true); - if (err) { - throw new FS.ErrnoError(err); - } - if (!parent.node_ops.rmdir) { - throw new FS.ErrnoError(ERRNO_CODES.EPERM); - } - if (FS.isMountpoint(node)) { - throw new FS.ErrnoError(ERRNO_CODES.EBUSY); - } - parent.node_ops.rmdir(parent, name); - FS.destroyNode(node); - }, - unlink: function(path) { - var lookup = FS.lookupPath(path, { parent: true }); - var parent = lookup.node; - var name = PATH.basename(path); - var node = FS.lookupNode(parent, name); - var err = FS.mayDelete(parent, name, false); - if (err) { - // POSIX says unlink should set EPERM, not EISDIR - if (err === ERRNO_CODES.EISDIR) err = ERRNO_CODES.EPERM; - throw new FS.ErrnoError(err); - } - if (!parent.node_ops.unlink) { - throw new FS.ErrnoError(ERRNO_CODES.EPERM); - } - if (FS.isMountpoint(node)) { - throw new FS.ErrnoError(ERRNO_CODES.EBUSY); - } - parent.node_ops.unlink(parent, name); - FS.destroyNode(node); - }, - readlink: function(path) { - var lookup = FS.lookupPath(path, { follow: false }); - var link = lookup.node; - if (!link.node_ops.readlink) { - throw new FS.ErrnoError(ERRNO_CODES.EINVAL); - } - return link.node_ops.readlink(link); - }, - stat: function(path, dontFollow) { - var lookup = FS.lookupPath(path, { follow: !dontFollow }); - var node = lookup.node; - if (!node.node_ops.getattr) { - throw new FS.ErrnoError(ERRNO_CODES.EPERM); - } - return node.node_ops.getattr(node); - }, - lstat: function(path) { - return VFS.stat(path, true); - }, - chmod: function(path, mode, dontFollow) { - var node; - if (typeof path === 'string') { - var lookup = FS.lookupPath(path, { follow: !dontFollow }); - node = lookup.node; - } else { - node = path; - } - if (!node.node_ops.setattr) { - throw new FS.ErrnoError(ERRNO_CODES.EPERM); - } - node.node_ops.setattr(node, { - mode: (mode & {{{ cDefine('S_IALLUGO') }}}) | (node.mode & ~{{{ cDefine('S_IALLUGO') }}}), - timestamp: Date.now() - }); - }, - lchmod: function(path, mode) { - VFS.chmod(path, mode, true); - }, - fchmod: function(fd, mode) { - var stream = FS.getStream(fd); - if (!stream) { - throw new FS.ErrnoError(ERRNO_CODES.EBADF); - } - VFS.chmod(stream.node, mode); - }, - chown: function(path, uid, gid, dontFollow) { - var node; - if (typeof path === 'string') { - var lookup = FS.lookupPath(path, { follow: !dontFollow }); - node = lookup.node; - } else { - node = path; - } - if (!node.node_ops.setattr) { - throw new FS.ErrnoError(ERRNO_CODES.EPERM); - } - node.node_ops.setattr(node, { - timestamp: Date.now() - // we ignore the uid / gid for now - }); - }, - lchown: function(path, uid, gid) { - VFS.chown(path, uid, gid, true); - }, - fchown: function(fd, uid, gid) { - var stream = FS.getStream(fd); - if (!stream) { - throw new FS.ErrnoError(ERRNO_CODES.EBADF); - } - VFS.chown(stream.node, uid, gid); - }, - truncate: function(path, len) { - if (len < 0) { - throw new FS.ErrnoError(ERRNO_CODES.EINVAL); - } - var node; - if (typeof path === 'string') { - var lookup = FS.lookupPath(path, { follow: true }); - node = lookup.node; - } else { - node = path; - } - if (!node.node_ops.setattr) { - throw new FS.ErrnoError(ERRNO_CODES.EPERM); - } - if (FS.isDir(node.mode)) { - throw new FS.ErrnoError(ERRNO_CODES.EISDIR); - } - if (!FS.isFile(node.mode)) { - throw new FS.ErrnoError(ERRNO_CODES.EINVAL); - } - var err = FS.nodePermissions(node, 'w'); - if (err) { - throw new FS.ErrnoError(err); - } - node.node_ops.setattr(node, { - size: len, - timestamp: Date.now() - }); - }, - ftruncate: function(fd, len) { - var stream = FS.getStream(fd); - if (!stream) { - throw new FS.ErrnoError(ERRNO_CODES.EBADF); - } - if ((stream.flags & {{{ cDefine('O_ACCMODE') }}}) === {{{ cDefine('O_RDONLY')}}}) { - throw new FS.ErrnoError(ERRNO_CODES.EINVAL); - } - VFS.truncate(stream.node, len); - }, - utime: function(path, atime, mtime) { - var lookup = FS.lookupPath(path, { follow: true }); - var node = lookup.node; - node.node_ops.setattr(node, { - timestamp: Math.max(atime, mtime) - }); - }, - open: function(path, flags, mode, fd_start, fd_end) { - path = PATH.normalize(path); - flags = typeof flags === 'string' ? FS.modeStringToFlags(flags) : flags; - if ((flags & {{{ cDefine('O_CREAT') }}})) { - mode = (mode & {{{ cDefine('S_IALLUGO') }}}) | {{{ cDefine('S_IFREG') }}}; - } else { - mode = 0; - } - var node; - try { - var lookup = FS.lookupPath(path, { - follow: !(flags & {{{ cDefine('O_NOFOLLOW') }}}) - }); - node = lookup.node; - path = lookup.path; - } catch (e) { - // ignore - } - // perhaps we need to create the node - if ((flags & {{{ cDefine('O_CREAT') }}})) { - if (node) { - // if O_CREAT and O_EXCL are set, error out if the node already exists - if ((flags & {{{ cDefine('O_EXCL') }}})) { - throw new FS.ErrnoError(ERRNO_CODES.EEXIST); - } - } else { - // node doesn't exist, try to create it - node = VFS.mknod(path, mode, 0); - } - } - if (!node) { - throw new FS.ErrnoError(ERRNO_CODES.ENOENT); - } - // can't truncate a device - if (FS.isChrdev(node.mode)) { - flags &= ~{{{ cDefine('O_TRUNC') }}}; - } - // check permissions - var err = FS.mayOpen(node, flags); - if (err) { - throw new FS.ErrnoError(err); - } - // do truncation if necessary - if ((flags & {{{ cDefine('O_TRUNC')}}})) { - VFS.truncate(node, 0); - } - // register the stream with the filesystem - var stream = FS.createStream({ - path: path, - node: node, - flags: flags, - seekable: true, - position: 0, - stream_ops: node.stream_ops, - // used by the file family libc calls (fopen, fwrite, ferror, etc.) - ungotten: [], - error: false - }, fd_start, fd_end); - // call the new stream's open function - if (stream.stream_ops.open) { - stream.stream_ops.open(stream); - } - return stream; - }, - close: function(stream) { - try { - if (stream.stream_ops.close) { - stream.stream_ops.close(stream); - } - } catch (e) { - throw e; - } finally { - FS.closeStream(stream.fd); - } - }, - llseek: function(stream, offset, whence) { - if (!stream.seekable || !stream.stream_ops.llseek) { - throw new FS.ErrnoError(ERRNO_CODES.ESPIPE); - } - return stream.stream_ops.llseek(stream, offset, whence); - }, - readdir: function(stream) { - if (!stream.stream_ops.readdir) { - throw new FS.ErrnoError(ERRNO_CODES.ENOTDIR); - } - return stream.stream_ops.readdir(stream); - }, - read: function(stream, buffer, offset, length, position) { - if (length < 0 || position < 0) { - throw new FS.ErrnoError(ERRNO_CODES.EINVAL); - } - if ((stream.flags & {{{ cDefine('O_ACCMODE') }}}) === {{{ cDefine('O_WRONLY')}}}) { - throw new FS.ErrnoError(ERRNO_CODES.EBADF); - } - if (FS.isDir(stream.node.mode)) { - throw new FS.ErrnoError(ERRNO_CODES.EISDIR); - } - if (!stream.stream_ops.read) { - throw new FS.ErrnoError(ERRNO_CODES.EINVAL); - } - var seeking = true; - if (typeof position === 'undefined') { - position = stream.position; - seeking = false; - } else if (!stream.seekable) { - throw new FS.ErrnoError(ERRNO_CODES.ESPIPE); - } - var bytesRead = stream.stream_ops.read(stream, buffer, offset, length, position); - if (!seeking) stream.position += bytesRead; - return bytesRead; - }, - write: function(stream, buffer, offset, length, position) { - if (length < 0 || position < 0) { - throw new FS.ErrnoError(ERRNO_CODES.EINVAL); - } - if ((stream.flags & {{{ cDefine('O_ACCMODE') }}}) === {{{ cDefine('O_RDONLY')}}}) { - throw new FS.ErrnoError(ERRNO_CODES.EBADF); - } - if (FS.isDir(stream.node.mode)) { - throw new FS.ErrnoError(ERRNO_CODES.EISDIR); - } - if (!stream.stream_ops.write) { - throw new FS.ErrnoError(ERRNO_CODES.EINVAL); - } - var seeking = true; - if (typeof position === 'undefined') { - position = stream.position; - seeking = false; - } else if (!stream.seekable) { - throw new FS.ErrnoError(ERRNO_CODES.ESPIPE); - } - if (stream.flags & {{{ cDefine('O_APPEND') }}}) { - // seek to the end before writing in append mode - VFS.llseek(stream, 0, {{{ cDefine('SEEK_END') }}}); - } - var bytesWritten = stream.stream_ops.write(stream, buffer, offset, length, position); - if (!seeking) stream.position += bytesWritten; - return bytesWritten; - }, - allocate: function(stream, offset, length) { - if (offset < 0 || length <= 0) { - throw new FS.ErrnoError(ERRNO_CODES.EINVAL); - } - if ((stream.flags & {{{ cDefine('O_ACCMODE') }}}) === {{{ cDefine('O_RDONLY')}}}) { - throw new FS.ErrnoError(ERRNO_CODES.EBADF); - } - if (!FS.isFile(stream.node.mode) && !FS.isDir(node.mode)) { - throw new FS.ErrnoError(ERRNO_CODES.ENODEV); - } - if (!stream.stream_ops.allocate) { - throw new FS.ErrnoError(ERRNO_CODES.EOPNOTSUPP); - } - stream.stream_ops.allocate(stream, offset, length); - }, - mmap: function(stream, buffer, offset, length, position, prot, flags) { - // TODO if PROT is PROT_WRITE, make sure we have write access - if ((stream.flags & {{{ cDefine('O_ACCMODE') }}}) === {{{ cDefine('O_WRONLY')}}}) { - throw new FS.ErrnoError(ERRNO_CODES.EACCES); - } - if (!stream.stream_ops.mmap) { - throw new FS.errnoError(ERRNO_CODES.ENODEV); - } - return stream.stream_ops.mmap(stream, buffer, offset, length, position, prot, flags); - } - }, - - $MEMFS__deps: ['$FS'], - $MEMFS: { - mount: function(mount) { - return MEMFS.create_node(null, '/', {{{ cDefine('S_IFDIR') }}} | 0777, 0); - }, - create_node: function(parent, name, mode, dev) { - if (FS.isBlkdev(mode) || FS.isFIFO(mode)) { - // no supported - throw new FS.ErrnoError(ERRNO_CODES.EPERM); - } - var node = FS.createNode(parent, name, mode, dev); - node.node_ops = MEMFS.node_ops; - if (FS.isDir(node.mode)) { - node.stream_ops = MEMFS.stream_ops; - node.contents = {}; - } else if (FS.isFile(node.mode)) { - node.stream_ops = MEMFS.stream_ops; - node.contents = []; - } else if (FS.isLink(node.mode)) { - node.stream_ops = MEMFS.stream_ops; - } else if (FS.isChrdev(node.mode)) { - node.stream_ops = FS.chrdev_stream_ops; - } - node.timestamp = Date.now(); - // add the new node to the parent - if (parent) { - parent.contents[name] = node; - } - return node; - }, - node_ops: { - getattr: function(node) { - var attr = {}; - // device numbers reuse inode numbers. - attr.dev = FS.isChrdev(node.mode) ? node.id : 1; - attr.ino = node.id; - attr.mode = node.mode; - attr.nlink = 1; - attr.uid = 0; - attr.gid = 0; - attr.rdev = node.rdev; - if (FS.isDir(node.mode)) { - attr.size = 4096; - } else if (FS.isFile(node.mode)) { - attr.size = node.contents.length; - } else if (FS.isLink(node.mode)) { - attr.size = node.link.length; - } else { - attr.size = 0; - } - attr.atime = new Date(node.timestamp); - attr.mtime = new Date(node.timestamp); - attr.ctime = new Date(node.timestamp); - // NOTE: In our implementation, st_blocks = Math.ceil(st_size/st_blksize), - // but this is not required by the standard. - attr.blksize = 4096; - attr.blocks = Math.ceil(attr.size / attr.blksize); - return attr; - }, - setattr: function(node, attr) { - if (attr.mode !== undefined) { - node.mode = attr.mode; - } - if (attr.timestamp !== undefined) { - node.timestamp = attr.timestamp; - } - if (attr.size !== undefined) { - var contents = node.contents; - if (attr.size < contents.length) contents.length = attr.size; - else while (attr.size > contents.length) contents.push(0); - } - }, - lookup: function(parent, name) { - throw new FS.ErrnoError(ERRNO_CODES.ENOENT); - }, - mknod: function(parent, name, mode, dev) { - return MEMFS.create_node(parent, name, mode, dev); - }, - rename: function(old_node, new_dir, new_name) { - // if we're overwriting a directory at new_name, make sure it's empty. - if (FS.isDir(old_node.mode)) { - var new_node; - try { - new_node = FS.lookupNode(new_dir, new_name); - } catch (e) { - } - if (new_node) { - for (var i in new_node.contents) { - throw new FS.ErrnoError(ERRNO_CODES.ENOTEMPTY); - } - } - } - // do the internal rewiring - delete old_node.parent.contents[old_node.name]; - old_node.name = new_name; - new_dir.contents[new_name] = old_node; - }, - unlink: function(parent, name) { - delete parent.contents[name]; - }, - rmdir: function(parent, name) { - var node = FS.lookupNode(parent, name); - for (var i in node.contents) { - throw new FS.ErrnoError(ERRNO_CODES.ENOTEMPTY); - } - delete parent.contents[name]; - }, - symlink: function(parent, newname, oldpath) { - var node = MEMFS.create_node(parent, newname, 0777 | {{{ cDefine('S_IFLNK') }}}, 0); - node.link = oldpath; - return node; - }, - readlink: function(node) { - if (!FS.isLink(node.mode)) { - throw new FS.ErrnoError(ERRNO_CODES.EINVAL); - } - return node.link; - }, - }, - stream_ops: { - open: function(stream) { - if (FS.isDir(stream.node.mode)) { - // cache off the directory entries when open'd - var entries = ['.', '..'] - for (var key in stream.node.contents) { - if (!stream.node.contents.hasOwnProperty(key)) { - continue; - } - entries.push(key); - } - stream.entries = entries; - } - }, - read: function(stream, buffer, offset, length, position) { - var contents = stream.node.contents; - var size = Math.min(contents.length - position, length); -#if USE_TYPED_ARRAYS == 2 - if (contents.subarray) { // typed array - buffer.set(contents.subarray(position, position + size), offset); - } else -#endif - { - for (var i = 0; i < size; i++) { - buffer[offset + i] = contents[position + i]; - } - } - return size; - }, - write: function(stream, buffer, offset, length, position) { - var contents = stream.node.contents; - while (contents.length < position) contents.push(0); - for (var i = 0; i < length; i++) { - contents[position + i] = buffer[offset + i]; - } - stream.node.timestamp = Date.now(); - return length; - }, - llseek: function(stream, offset, whence) { - var position = offset; - if (whence === 1) { // SEEK_CUR. - position += stream.position; - } else if (whence === 2) { // SEEK_END. - if (FS.isFile(stream.node.mode)) { - position += stream.node.contents.length; - } - } - if (position < 0) { - throw new FS.ErrnoError(ERRNO_CODES.EINVAL); - } - stream.ungotten = []; - stream.position = position; - return position; - }, - readdir: function(stream) { - return stream.entries; - }, - allocate: function(stream, offset, length) { - var contents = stream.node.contents; - var limit = offset + length; - while (limit > contents.length) contents.push(0); - }, - mmap: function(stream, buffer, offset, length, position, prot, flags) { - if (!FS.isFile(stream.node.mode)) { - throw new FS.ErrnoError(ERRNO_CODES.ENODEV); - } - var ptr; - var allocated; - var contents = stream.node.contents; - // Only make a new copy when MAP_PRIVATE is specified. - if (!(flags & {{{ cDefine('MAP_PRIVATE') }}})) { - // We can't emulate MAP_SHARED when the file is not backed by the buffer - // we're mapping to (e.g. the HEAP buffer). - assert(contents.buffer === buffer || contents.buffer === buffer.buffer); - allocated = false; - ptr = contents.byteOffset; - } else { - // Try to avoid unnecessary slices. - if (position > 0 || position + length < contents.length) { - if (contents.subarray) { - contents = contents.subarray(position, position + length); - } else { - contents = Array.prototype.slice.call(contents, position, position + length); - } - } - allocated = true; - ptr = _malloc(length); - if (!ptr) { - throw new FS.ErrnoError(ERRNO_CODES.ENOMEM); - } - buffer.set(contents, ptr); - } - return { ptr: ptr, allocated: allocated }; - }, - } - }, - - $SOCKFS__postset: '__ATINIT__.push({ func: function() { SOCKFS.root = VFS.mount(SOCKFS, {}, null); } });', - $SOCKFS__deps: ['$FS'], - $SOCKFS: { - mount: function(mount) { - var node = FS.createNode(null, '/', {{{ cDefine('S_IFDIR') }}} | 0777, 0); - node.node_ops = SOCKFS.node_ops; - node.stream_ops = SOCKFS.stream_ops; - return node; - }, - node_ops: { - }, - stream_ops: { - }, - websocket_sock_ops: { - } - }, - - $TTY__deps: ['$FS'], - $TTY: { - ttys: [], - register: function(dev, ops) { - TTY.ttys[dev] = { input: [], output: [], ops: ops }; - FS.registerDevice(dev, TTY.stream_ops); - }, - stream_ops: { - open: function(stream) { - // this wouldn't be required if the library wasn't eval'd at first... - if (!TTY.utf8) { - TTY.utf8 = new Runtime.UTF8Processor(); - } - var tty = TTY.ttys[stream.node.rdev]; - if (!tty) { - throw new FS.ErrnoError(ERRNO_CODES.ENODEV); - } - stream.tty = tty; - stream.seekable = false; - }, - close: function(stream) { - // flush any pending line data - if (stream.tty.output.length) { - stream.tty.ops.put_char(stream.tty, {{{ charCode('\n') }}}); - } - }, - read: function(stream, buffer, offset, length, pos /* ignored */) { - if (!stream.tty || !stream.tty.ops.get_char) { - throw new FS.ErrnoError(ERRNO_CODES.ENXIO); - } - var bytesRead = 0; - for (var i = 0; i < length; i++) { - var result; - try { - result = stream.tty.ops.get_char(stream.tty); - } catch (e) { - throw new FS.ErrnoError(ERRNO_CODES.EIO); - } - if (result === undefined && bytesRead === 0) { - throw new FS.ErrnoError(ERRNO_CODES.EAGAIN); - } - if (result === null || result === undefined) break; - bytesRead++; - buffer[offset+i] = result; - } - if (bytesRead) { - stream.node.timestamp = Date.now(); - } - return bytesRead; - }, - write: function(stream, buffer, offset, length, pos) { - if (!stream.tty || !stream.tty.ops.put_char) { - throw new FS.ErrnoError(ERRNO_CODES.ENXIO); - } - for (var i = 0; i < length; i++) { - try { - stream.tty.ops.put_char(stream.tty, buffer[offset+i]); - } catch (e) { - throw new FS.ErrnoError(ERRNO_CODES.EIO); - } - } - if (length) { - stream.node.timestamp = Date.now(); - } - return i; - } - }, - // NOTE: This is weird to support stdout and stderr - // overrides in addition to print and printErr overrides. - default_tty_ops: { - get_char: function(tty) { - if (!tty.input.length) { - var result = null; - if (ENVIRONMENT_IS_NODE) { - if (process.stdin.destroyed) { - return undefined; - } - result = process.stdin.read(); - } else if (typeof window != 'undefined' && - typeof window.prompt == 'function') { - // Browser. - result = window.prompt('Input: '); // returns null on cancel - if (result !== null) { - result += '\n'; - } - } else if (typeof readline == 'function') { - // Command line. - result = readline(); - if (result !== null) { - result += '\n'; - } - } - if (!result) { - return null; - } - tty.input = intArrayFromString(result, true); - } - return tty.input.shift(); - }, - put_char: function(tty, val) { - if (val === null || val === {{{ charCode('\n') }}}) { - Module['print'](tty.output.join('')); - tty.output = []; - } else { - tty.output.push(TTY.utf8.processCChar(val)); - } - } - }, - default_tty1_ops: { - put_char: function(tty, val) { - if (val === null || val === {{{ charCode('\n') }}}) { - Module['printErr'](tty.output.join('')); - tty.output = []; - } else { - tty.output.push(TTY.utf8.processCChar(val)); - } - } - } - }, -#endif - - // ========================================================================== // dirent.h // ========================================================================== @@ -1825,7 +100,7 @@ LibraryManager.library = { } var entries; try { - entries = VFS.readdir(stream); + entries = FS.readdir(stream); } catch (e) { return FS.handleFSError(e); } @@ -1904,7 +179,7 @@ LibraryManager.library = { } path = Pointer_stringify(path); try { - VFS.utime(path, time, time); + FS.utime(path, time, time); return 0; } catch (e) { FS.handleFSError(e); @@ -2004,7 +279,7 @@ LibraryManager.library = { // used in client code. path = typeof path !== 'string' ? Pointer_stringify(path) : path; try { - var stat = dontResolveLastLink ? VFS.lstat(path) : VFS.stat(path); + var stat = dontResolveLastLink ? FS.lstat(path) : FS.stat(path); {{{ makeSetValue('buf', '___stat_struct_layout.st_dev', 'stat.dev', 'i32') }}}; {{{ makeSetValue('buf', '___stat_struct_layout.st_ino', 'stat.ino', 'i32') }}} {{{ makeSetValue('buf', '___stat_struct_layout.st_mode', 'stat.mode', 'i32') }}} @@ -2054,7 +329,7 @@ LibraryManager.library = { return -1; } try { - VFS.mknod(path, mode, dev); + FS.mknod(path, mode, dev); return 0; } catch (e) { FS.handleFSError(e); @@ -2067,7 +342,7 @@ LibraryManager.library = { // http://pubs.opengroup.org/onlinepubs/7908799/xsh/mkdir.html path = Pointer_stringify(path); try { - VFS.mkdir(path, mode, 0); + FS.mkdir(path, mode, 0); return 0; } catch (e) { FS.handleFSError(e); @@ -2093,7 +368,7 @@ LibraryManager.library = { // used in client code. path = typeof path !== 'string' ? Pointer_stringify(path) : path; try { - VFS.chmod(path, mode); + FS.chmod(path, mode); return 0; } catch (e) { FS.handleFSError(e); @@ -2105,7 +380,7 @@ LibraryManager.library = { // int fchmod(int fildes, mode_t mode); // http://pubs.opengroup.org/onlinepubs/7908799/xsh/fchmod.html try { - VFS.fchmod(fildes, mode); + FS.fchmod(fildes, mode); return 0; } catch (e) { FS.handleFSError(e); @@ -2116,7 +391,7 @@ LibraryManager.library = { lchmod: function(path, mode) { path = Pointer_stringify(path); try { - VFS.lchmod(path, mode); + FS.lchmod(path, mode); return 0; } catch (e) { FS.handleFSError(e); @@ -2208,7 +483,7 @@ LibraryManager.library = { var mode = {{{ makeGetValue('varargs', 0, 'i32') }}}; path = Pointer_stringify(path); try { - var stream = VFS.open(path, oflag, mode); + var stream = FS.open(path, oflag, mode); return stream.fd; } catch (e) { FS.handleFSError(e); @@ -2248,7 +523,7 @@ LibraryManager.library = { } var newStream; try { - newStream = VFS.open(stream.path, stream.flags, 0, arg); + newStream = FS.open(stream.path, stream.flags, 0, arg); } catch (e) { FS.handleFSError(e); return -1; @@ -2305,7 +580,7 @@ LibraryManager.library = { return -1; } try { - VFS.allocate(stream, offset, len); + FS.allocate(stream, offset, len); return 0; } catch (e) { FS.handleFSError(e); @@ -2423,7 +698,7 @@ LibraryManager.library = { // used in client code. if (typeof path !== 'string') path = Pointer_stringify(path); try { - VFS.chown(path, owner, group); + FS.chown(path, owner, group); return 0; } catch (e) { FS.handleFSError(e); @@ -2447,7 +722,7 @@ LibraryManager.library = { return -1; } try { - VFS.close(stream); + FS.close(stream); return 0; } catch (e) { FS.handleFSError(e);; @@ -2473,7 +748,7 @@ LibraryManager.library = { } else { _close(fildes2); try { - var stream2 = VFS.open(stream.path, stream.flags, 0, fildes2, fildes2); + var stream2 = FS.open(stream.path, stream.flags, 0, fildes2, fildes2); return stream2.fd; } catch (e) { FS.handleFSError(e); @@ -2486,7 +761,7 @@ LibraryManager.library = { // int fchown(int fildes, uid_t owner, gid_t group); // http://pubs.opengroup.org/onlinepubs/000095399/functions/fchown.html try { - VFS.fchown(fildes, owner, group); + FS.fchown(fildes, owner, group); return 0; } catch (e) { FS.handleFSError(e); @@ -2589,7 +864,7 @@ LibraryManager.library = { // NOTE: The path argument may be a string, to simplify ftruncate(). if (typeof path !== 'string') path = Pointer_stringify(path); try { - VFS.truncate(path, length); + FS.truncate(path, length); return 0; } catch (e) { FS.handleFSError(e); @@ -2601,7 +876,7 @@ LibraryManager.library = { // int ftruncate(int fildes, off_t length); // http://pubs.opengroup.org/onlinepubs/000095399/functions/ftruncate.html try { - VFS.ftruncate(fildes, length); + FS.ftruncate(fildes, length); return 0; } catch (e) { FS.handleFSError(e); @@ -2686,7 +961,7 @@ LibraryManager.library = { return -1; } try { - return VFS.llseek(stream, offset, whence); + return FS.llseek(stream, offset, whence); } catch (e) { FS.handleFSError(e); return -1; @@ -2717,7 +992,7 @@ LibraryManager.library = { SAFE_HEAP_FILL_HISTORY(buf, buf+nbyte, 'i8'); // VFS does not use makeSetValues, so we need to do it manually #endif #endif - return VFS.read(stream, slab, buf, nbyte, offset); + return FS.read(stream, slab, buf, nbyte, offset); } catch (e) { FS.handleFSError(e); return -1; @@ -2744,7 +1019,7 @@ LibraryManager.library = { SAFE_HEAP_FILL_HISTORY(buf, buf+nbyte, 'i8'); // VFS does not use makeSetValues, so we need to do it manually #endif #endif - return VFS.read(stream, slab, buf, nbyte); + return FS.read(stream, slab, buf, nbyte); } catch (e) { FS.handleFSError(e); return -1; @@ -2761,7 +1036,7 @@ LibraryManager.library = { // http://pubs.opengroup.org/onlinepubs/000095399/functions/rmdir.html path = Pointer_stringify(path); try { - VFS.rmdir(path); + FS.rmdir(path); return 0; } catch (e) { FS.handleFSError(e); @@ -2774,7 +1049,7 @@ LibraryManager.library = { // http://pubs.opengroup.org/onlinepubs/000095399/functions/unlink.html path = Pointer_stringify(path); try { - VFS.unlink(path); + FS.unlink(path); return 0; } catch (e) { FS.handleFSError(e); @@ -2811,7 +1086,7 @@ LibraryManager.library = { path1 = Pointer_stringify(path1); path2 = Pointer_stringify(path2); try { - VFS.symlink(path1, path2); + FS.symlink(path1, path2); return 0; } catch (e) { FS.handleFSError(e); @@ -2825,7 +1100,7 @@ LibraryManager.library = { path = Pointer_stringify(path); var str; try { - str = VFS.readlink(path); + str = FS.readlink(path); } catch (e) { FS.handleFSError(e); return -1; @@ -2850,7 +1125,7 @@ LibraryManager.library = { SAFE_HEAP_FILL_HISTORY(buf, buf+nbyte, 'i8'); // VFS does not use makeSetValues, so we need to do it manually #endif #endif - return VFS.write(stream, slab, buf, nbyte, offset); + return FS.write(stream, slab, buf, nbyte, offset); } catch (e) { FS.handleFSError(e); return -1; @@ -2877,7 +1152,7 @@ LibraryManager.library = { SAFE_HEAP_FILL_HISTORY(buf, buf+nbyte, 'i8'); // VFS does not use makeSetValues, so we need to do it manually #endif #endif - return VFS.write(stream, slab, buf, nbyte); + return FS.write(stream, slab, buf, nbyte); } catch (e) { FS.handleFSError(e); return -1; @@ -4347,7 +2622,7 @@ LibraryManager.library = { old_path = Pointer_stringify(old_path); new_path = Pointer_stringify(new_path); try { - VFS.rename(old_path, new_path); + FS.rename(old_path, new_path); return 0; } catch (e) { FS.handleFSError(e); @@ -4611,7 +2886,7 @@ LibraryManager.library = { var info = FS.getStream(stream); if (!info) return -1; try { - var res = VFS.mmap(info, HEAPU8, start, num, offset, prot, flags); + var res = FS.mmap(info, HEAPU8, start, num, offset, prot, flags); ptr = res.ptr; allocated = res.allocated; } catch (e) { diff --git a/src/library_fs.js b/src/library_fs.js new file mode 100644 index 0000000000000..167b69527c0bb --- /dev/null +++ b/src/library_fs.js @@ -0,0 +1,1367 @@ +mergeInto(LibraryManager.library, { + $FS__deps: ['$ERRNO_CODES', '$ERRNO_MESSAGES', '__setErrNo', '$VFS', '$PATH', '$TTY', '$MEMFS', 'stdin', 'stdout', 'stderr', 'fflush'], + $FS__postset: 'FS.staticInit();' + + '__ATINIT__.unshift({ func: function() { if (!Module["noFSInit"] && !FS.init.initialized) FS.init() } });' + + '__ATMAIN__.push({ func: function() { FS.ignorePermissions = false } });' + + '__ATEXIT__.push({ func: function() { FS.quit() } });' + + // export some names through closure + 'Module["FS_createFolder"] = FS.createFolder;' + + 'Module["FS_createPath"] = FS.createPath;' + + 'Module["FS_createDataFile"] = FS.createDataFile;' + + 'Module["FS_createPreloadedFile"] = FS.createPreloadedFile;' + + 'Module["FS_createLazyFile"] = FS.createLazyFile;' + + 'Module["FS_createLink"] = FS.createLink;' + + 'Module["FS_createDevice"] = FS.createDevice;', + $FS: { + root: null, + nodes: [null], + devices: [null], + streams: [null], + nextInode: 1, + name_table: new Array(4096), + currentPath: '/', + initialized: false, + // Whether we are currently ignoring permissions. Useful when preparing the + // filesystem and creating files inside read-only folders. + // This is set to false when the runtime is initialized, allowing you + // to modify the filesystem freely before run() is called. + ignorePermissions: true, + + ErrnoError: function(errno) { + this.errno = errno; + for (var key in ERRNO_CODES) { + if (ERRNO_CODES[key] === errno) { + this.code = key; + break; + } + } + this.message = ERRNO_MESSAGES[errno]; + }, + + handleFSError: function(e) { + if (!(e instanceof FS.ErrnoError)) throw e + ' : ' + new Error().stack; + return ___setErrNo(e.errno); + }, + + // + // nodes + // + hashName: function(parentid, name) { + var hash = 0; + for (var i = 0; i < name.length; i++) { + hash = ((hash << 5) - hash + name.charCodeAt(i)) | 0; + } + return (parentid + hash) % FS.name_table.length; + }, + hashAddNode: function(node) { + var hash = FS.hashName(node.parent.id, node.name); + node.name_next = FS.name_table[hash]; + FS.name_table[hash] = node; + }, + hashRemoveNode: function(node) { + var hash = FS.hashName(node.parent.id, node.name); + if (FS.name_table[hash] === node) { + FS.name_table[hash] = node.name_next; + } else { + var current = FS.name_table[hash]; + while (current) { + if (current.name_next === node) { + current.name_next = node.name_next; + break; + } + current = current.name_next; + } + } + }, + lookupNode: function(parent, name) { + var err = FS.mayLookup(parent); + if (err) { + throw new FS.ErrnoError(err); + } + var hash = FS.hashName(parent.id, name); + for (var node = FS.name_table[hash]; node; node = node.name_next) { + if (node.parent.id === parent.id && node.name === name) { + return node; + } + } + // if we failed to find it in the cache, call into the VFS + return FS.lookup(parent, name); + }, + createNode: function(parent, name, mode, rdev) { + var node = { + id: FS.nextInode++, + name: name, + mode: mode, + node_ops: {}, + stream_ops: {}, + rdev: rdev, + parent: null, + mount: null + }; + if (!parent) { + parent = node; // root node sets parent to itself + } + node.parent = parent; + node.mount = parent.mount; + // compatibility + var readMode = {{{ cDefine('S_IRUGO') }}} | {{{ cDefine('S_IXUGO') }}}; + var writeMode = {{{ cDefine('S_IWUGO') }}}; + // NOTE we must use Object.defineProperties instead of individual calls to + // Object.defineProperty in order to make closure compiler happy + Object.defineProperties(node, { + read: { + get: function() { return (node.mode & readMode) === readMode; }, + set: function(val) { val ? node.mode |= readMode : node.mode &= ~readMode; } + }, + write: { + get: function() { return (node.mode & writeMode) === writeMode; }, + set: function(val) { val ? node.mode |= writeMode : node.mode &= ~writeMode; } + }, + isFolder: { + get: function() { return FS.isDir(node.mode); }, + }, + isDevice: { + get: function() { return FS.isChrdev(node.mode); }, + }, + }); + FS.hashAddNode(node); + return node; + }, + destroyNode: function(node) { + FS.hashRemoveNode(node); + }, + isRoot: function(node) { + return node === node.parent; + }, + isMountpoint: function(node) { + return node.mounted; + }, + isFile: function(mode) { + return (mode & {{{ cDefine('S_IFMT') }}}) === {{{ cDefine('S_IFREG') }}}; + }, + isDir: function(mode) { + return (mode & {{{ cDefine('S_IFMT') }}}) === {{{ cDefine('S_IFDIR') }}}; + }, + isLink: function(mode) { + return (mode & {{{ cDefine('S_IFMT') }}}) === {{{ cDefine('S_IFLNK') }}}; + }, + isChrdev: function(mode) { + return (mode & {{{ cDefine('S_IFMT') }}}) === {{{ cDefine('S_IFCHR') }}}; + }, + isBlkdev: function(mode) { + return (mode & {{{ cDefine('S_IFMT') }}}) === {{{ cDefine('S_IFBLK') }}}; + }, + isFIFO: function(mode) { + return (mode & {{{ cDefine('S_IFMT') }}}) === {{{ cDefine('S_IFIFO') }}}; + }, + + // + // paths + // + cwd: function() { + return FS.currentPath; + }, + lookupPath: function(path, opts) { + path = PATH.resolve(FS.currentPath, path); + opts = opts || { recurse_count: 0 }; + + if (opts.recurse_count > 8) { // max recursive lookup of 8 + throw new FS.ErrnoError(ERRNO_CODES.ELOOP); + } + + // split the path + var parts = PATH.normalizeArray(path.split('/').filter(function(p) { + return !!p; + }), false); + + // start at the root + var current = FS.root; + var current_path = '/'; + + for (var i = 0; i < parts.length; i++) { + var islast = (i === parts.length-1); + if (islast && opts.parent) { + // stop resolving + break; + } + + current = FS.lookupNode(current, parts[i]); + current_path = PATH.join(current_path, parts[i]); + + // jump to the mount's root node if this is a mountpoint + if (FS.isMountpoint(current)) { + current = current.mount.root; + } + + // follow symlinks + // by default, lookupPath will not follow a symlink if it is the final path component. + // setting opts.follow = true will override this behavior. + if (!islast || opts.follow) { + var count = 0; + while (FS.isLink(current.mode)) { + var link = FS.readlink(current_path); + current_path = PATH.resolve(PATH.dirname(current_path), link); + + var lookup = FS.lookupPath(current_path, { recurse_count: opts.recurse_count }); + current = lookup.node; + + if (count++ > 40) { // limit max consecutive symlinks to 40 (SYMLOOP_MAX). + throw new FS.ErrnoError(ERRNO_CODES.ELOOP); + } + } + } + } + + return { path: current_path, node: current }; + }, + getPath: function(node) { + var path; + while (true) { + if (FS.isRoot(node)) { + return path ? PATH.join(node.mount.mountpoint, path) : node.mount.mountpoint; + } + path = path ? PATH.join(node.name, path) : node.name; + node = node.parent; + } + }, + + // + // permissions + // + flagModes: { + '"r"': {{{ cDefine('O_RDONLY') }}}, + '"rs"': {{{ cDefine('O_RDONLY') }}} | {{{ cDefine('O_SYNC') }}}, + '"r+"': {{{ cDefine('O_RDWR') }}}, + '"w"': {{{ cDefine('O_TRUNC') }}} | {{{ cDefine('O_CREAT') }}} | {{{ cDefine('O_WRONLY') }}}, + '"wx"': {{{ cDefine('O_TRUNC') }}} | {{{ cDefine('O_CREAT') }}} | {{{ cDefine('O_WRONLY') }}} | {{{ cDefine('O_EXCL') }}}, + '"xw"': {{{ cDefine('O_TRUNC') }}} | {{{ cDefine('O_CREAT') }}} | {{{ cDefine('O_WRONLY') }}} | {{{ cDefine('O_EXCL') }}}, + '"w+"': {{{ cDefine('O_TRUNC') }}} | {{{ cDefine('O_CREAT') }}} | {{{ cDefine('O_RDWR') }}}, + '"wx+"': {{{ cDefine('O_TRUNC') }}} | {{{ cDefine('O_CREAT') }}} | {{{ cDefine('O_RDWR') }}} | {{{ cDefine('O_EXCL') }}}, + '"xw+"': {{{ cDefine('O_TRUNC') }}} | {{{ cDefine('O_CREAT') }}} | {{{ cDefine('O_RDWR') }}} | {{{ cDefine('O_EXCL') }}}, + '"a"': {{{ cDefine('O_APPEND') }}} | {{{ cDefine('O_CREAT') }}} | {{{ cDefine('O_WRONLY') }}}, + '"ax"': {{{ cDefine('O_APPEND') }}} | {{{ cDefine('O_CREAT') }}} | {{{ cDefine('O_WRONLY') }}} | {{{ cDefine('O_EXCL') }}}, + '"xa"': {{{ cDefine('O_APPEND') }}} | {{{ cDefine('O_CREAT') }}} | {{{ cDefine('O_WRONLY') }}} | {{{ cDefine('O_EXCL') }}}, + '"a+"': {{{ cDefine('O_APPEND') }}} | {{{ cDefine('O_CREAT') }}} | {{{ cDefine('O_RDWR') }}}, + '"ax+"': {{{ cDefine('O_APPEND') }}} | {{{ cDefine('O_CREAT') }}} | {{{ cDefine('O_RDWR') }}} | {{{ cDefine('O_EXCL') }}}, + '"xa+"': {{{ cDefine('O_APPEND') }}} | {{{ cDefine('O_CREAT') }}} | {{{ cDefine('O_RDWR') }}} | {{{ cDefine('O_EXCL') }}} + }, + // convert the 'r', 'r+', etc. to it's corresponding set of O_* flags + modeStringToFlags: function(str) { + var flags = FS.flagModes[str]; + if (typeof flags === 'undefined') { + throw new Error('Unknown file open mode: ' + str); + } + return flags; + }, + // convert O_* bitmask to a string for nodePermissions + flagsToPermissionString: function(flag) { + var accmode = flag & {{{ cDefine('O_ACCMODE') }}}; + var perms = ['r', 'w', 'rw'][accmode]; + if ((flag & {{{ cDefine('O_TRUNC') }}})) { + perms += 'w'; + } + return perms; + }, + nodePermissions: function(node, perms) { + if (FS.ignorePermissions) { + return 0; + } + // return 0 if any user, group or owner bits are set. + if (perms.indexOf('r') !== -1 && !(node.mode & {{{ cDefine('S_IRUGO') }}})) { + return ERRNO_CODES.EACCES; + } else if (perms.indexOf('w') !== -1 && !(node.mode & {{{ cDefine('S_IWUGO') }}})) { + return ERRNO_CODES.EACCES; + } else if (perms.indexOf('x') !== -1 && !(node.mode & {{{ cDefine('S_IXUGO') }}})) { + return ERRNO_CODES.EACCES; + } + return 0; + }, + mayLookup: function(dir) { + return FS.nodePermissions(dir, 'x'); + }, + mayMknod: function(mode) { + switch (mode & {{{ cDefine('S_IFMT') }}}) { + case {{{ cDefine('S_IFREG') }}}: + case {{{ cDefine('S_IFCHR') }}}: + case {{{ cDefine('S_IFBLK') }}}: + case {{{ cDefine('S_IFIFO') }}}: + case {{{ cDefine('S_IFSOCK') }}}: + return 0; + default: + return ERRNO_CODES.EINVAL; + } + }, + mayCreate: function(dir, name) { + try { + var node = FS.lookupNode(dir, name); + return ERRNO_CODES.EEXIST; + } catch (e) { + } + return FS.nodePermissions(dir, 'wx'); + }, + mayDelete: function(dir, name, isdir) { + var node; + try { + node = FS.lookupNode(dir, name); + } catch (e) { + return e.errno; + } + var err = FS.nodePermissions(dir, 'wx'); + if (err) { + return err; + } + if (isdir) { + if (!FS.isDir(node.mode)) { + return ERRNO_CODES.ENOTDIR; + } + if (FS.isRoot(node) || FS.getPath(node) === FS.currentPath) { + return ERRNO_CODES.EBUSY; + } + } else { + if (FS.isDir(node.mode)) { + return ERRNO_CODES.EISDIR; + } + } + return 0; + }, + mayOpen: function(node, flags) { + if (!node) { + return ERRNO_CODES.ENOENT; + } + if (FS.isLink(node.mode)) { + return ERRNO_CODES.ELOOP; + } else if (FS.isDir(node.mode)) { + if ((flags & {{{ cDefine('O_ACCMODE') }}}) !== {{{ cDefine('O_RDONLY')}}} || // opening for write + (flags & {{{ cDefine('O_TRUNC') }}})) { + return ERRNO_CODES.EISDIR; + } + } + return FS.nodePermissions(node, FS.flagsToPermissionString(flags)); + }, + + // + // devices + // + // each character device consists of a device id + stream operations. + // when a character device node is created (e.g. /dev/stdin) it is + // assigned a device id that lets us map back to the actual device. + // by default, each character device stream (e.g. _stdin) uses chrdev_stream_ops. + // however, once opened, the stream's operations are overridden with + // the operations of the device its underlying node maps back to. + chrdev_stream_ops: { + open: function(stream) { + var device = FS.getDevice(stream.node.rdev); + // override node's stream ops with the device's + stream.stream_ops = device.stream_ops; + // forward the open call + if (stream.stream_ops.open) { + stream.stream_ops.open(stream); + } + }, + llseek: function() { + throw new FS.ErrnoError(ERRNO_CODES.ESPIPE); + } + }, + major: function(dev) { + return ((dev) >> 8); + }, + minor: function(dev) { + return ((dev) & 0xff); + }, + makedev: function(ma, mi) { + return ((ma) << 8 | (mi)); + }, + registerDevice: function(dev, ops) { + FS.devices[dev] = { stream_ops: ops }; + }, + getDevice: function(dev) { + return FS.devices[dev]; + }, + + // + // streams + // + MAX_OPEN_FDS: 4096, + nextfd: function(fd_start, fd_end) { + fd_start = fd_start || 1; + fd_end = fd_end || FS.MAX_OPEN_FDS; + for (var fd = fd_start; fd <= fd_end; fd++) { + if (!FS.streams[fd]) { + return fd; + } + } + throw new FS.ErrnoError(ERRNO_CODES.EMFILE); + }, + getStream: function(fd) { + return FS.streams[fd]; + }, + createStream: function(stream, fd_start, fd_end) { + var fd = FS.nextfd(fd_start, fd_end); + stream.fd = fd; + // compatibility + Object.defineProperties(stream, { + object: { + get: function() { return stream.node; }, + set: function(val) { stream.node = val; } + }, + isRead: { + get: function() { return (stream.flags & {{{ cDefine('O_ACCMODE') }}}) !== {{{ cDefine('O_WRONLY') }}}; } + }, + isWrite: { + get: function() { return (stream.flags & {{{ cDefine('O_ACCMODE') }}}) !== {{{ cDefine('O_RDONLY') }}}; } + }, + isAppend: { + get: function() { return (stream.flags & {{{ cDefine('O_APPEND') }}}); } + } + }); + FS.streams[fd] = stream; + return stream; + }, + closeStream: function(fd) { + FS.streams[fd] = null; + }, + + // + // compatibility + // + getMode: function(canRead, canWrite) { + var mode = 0; + if (canRead) mode |= {{{ cDefine('S_IRUGO') }}} | {{{ cDefine('S_IXUGO') }}}; + if (canWrite) mode |= {{{ cDefine('S_IWUGO') }}}; + return mode; + }, + joinPath: function(parts, forceRelative) { + var path = PATH.join.apply(null, parts); + if (forceRelative && path[0] == '/') path = path.substr(1); + return path; + }, + absolutePath: function(relative, base) { + return PATH.resolve(base, relative); + }, + standardizePath: function(path) { + return PATH.normalize(path); + }, + findObject: function(path, dontResolveLastLink) { + var ret = FS.analyzePath(path, dontResolveLastLink); + if (ret.exists) { + return ret.object; + } else { + ___setErrNo(ret.error); + return null; + } + }, + analyzePath: function(path, dontResolveLastLink) { + // operate from within the context of the symlink's target + try { + var lookup = FS.lookupPath(path, { follow: !dontResolveLastLink }); + path = lookup.path; + } catch (e) { + } + var ret = { + isRoot: false, exists: false, error: 0, name: null, path: null, object: null, + parentExists: false, parentPath: null, parentObject: null + }; + try { + var lookup = FS.lookupPath(path, { parent: true }); + ret.parentExists = true; + ret.parentPath = lookup.path; + ret.parentObject = lookup.node; + ret.name = PATH.basename(path); + lookup = FS.lookupPath(path, { follow: !dontResolveLastLink }); + ret.exists = true; + ret.path = lookup.path; + ret.object = lookup.node; + ret.name = lookup.node.name; + ret.isRoot = lookup.path === '/'; + } catch (e) { + ret.error = e.errno; + }; + return ret; + }, + createFolder: function(parent, name, canRead, canWrite) { + var path = PATH.join(typeof parent === 'string' ? parent : FS.getPath(parent), name); + var mode = FS.getMode(canRead, canWrite); + return FS.mkdir(path, mode); + }, + createPath: function(parent, path, canRead, canWrite) { + parent = typeof parent === 'string' ? parent : FS.getPath(parent); + var parts = path.split('/').reverse(); + while (parts.length) { + var part = parts.pop(); + if (!part) continue; + var current = PATH.join(parent, part); + try { + FS.mkdir(current, 0777); + } catch (e) { + // ignore EEXIST + } + parent = current; + } + return current; + }, + createFile: function(parent, name, properties, canRead, canWrite) { + var path = PATH.join(typeof parent === 'string' ? parent : FS.getPath(parent), name); + var mode = FS.getMode(canRead, canWrite); + return FS.create(path, mode); + }, + createDataFile: function(parent, name, data, canRead, canWrite) { + var path = PATH.join(typeof parent === 'string' ? parent : FS.getPath(parent), name); + var mode = FS.getMode(canRead, canWrite); + var node = FS.create(path, mode); + if (data) { + if (typeof data === 'string') { + var arr = new Array(data.length); + for (var i = 0, len = data.length; i < len; ++i) arr[i] = data.charCodeAt(i); + data = arr; + } + // make sure we can write to the file + FS.chmod(path, mode | {{{ cDefine('S_IWUGO') }}}); + var stream = FS.open(path, 'w'); + FS.write(stream, data, 0, data.length, 0); + FS.close(stream); + FS.chmod(path, mode); + } + return node; + }, + createDevice: function(parent, name, input, output) { + var path = PATH.join(typeof parent === 'string' ? parent : FS.getPath(parent), name); + var mode = input && output ? 0777 : (input ? 0333 : 0555); + if (!FS.createDevice.major) FS.createDevice.major = 64; + var dev = FS.makedev(FS.createDevice.major++, 0); + // Create a fake device that a set of stream ops to emulate + // the old behavior. + FS.registerDevice(dev, { + open: function(stream) { + stream.seekable = false; + }, + close: function(stream) { + // flush any pending line data + if (output && output.buffer && output.buffer.length) { + output({{{ charCode('\n') }}}); + } + }, + read: function(stream, buffer, offset, length, pos /* ignored */) { + var bytesRead = 0; + for (var i = 0; i < length; i++) { + var result; + try { + result = input(); + } catch (e) { + throw new FS.ErrnoError(ERRNO_CODES.EIO); + } + if (result === undefined && bytesRead === 0) { + throw new FS.ErrnoError(ERRNO_CODES.EAGAIN); + } + if (result === null || result === undefined) break; + bytesRead++; + buffer[offset+i] = result; + } + if (bytesRead) { + stream.node.timestamp = Date.now(); + } + return bytesRead; + }, + write: function(stream, buffer, offset, length, pos) { + for (var i = 0; i < length; i++) { + try { + output(buffer[offset+i]); + } catch (e) { + throw new FS.ErrnoError(ERRNO_CODES.EIO); + } + } + if (length) { + stream.node.timestamp = Date.now(); + } + return i; + } + }); + return FS.mkdev(path, mode, dev); + }, + createLink: function(parent, name, target, canRead, canWrite) { + var path = PATH.join(typeof parent === 'string' ? parent : FS.getPath(parent), name); + return FS.symlink(target, path); + }, + // Makes sure a file's contents are loaded. Returns whether the file has + // been loaded successfully. No-op for files that have been loaded already. + forceLoadFile: function(obj) { + if (obj.isDevice || obj.isFolder || obj.link || obj.contents) return true; + var success = true; + if (typeof XMLHttpRequest !== 'undefined') { + throw new Error("Lazy loading should have been performed (contents set) in createLazyFile, but it was not. Lazy loading only works in web workers. Use --embed-file or --preload-file in emcc on the main thread."); + } else if (Module['read']) { + // Command-line. + try { + // WARNING: Can't read binary files in V8's d8 or tracemonkey's js, as + // read() will try to parse UTF8. + obj.contents = intArrayFromString(Module['read'](obj.url), true); + } catch (e) { + success = false; + } + } else { + throw new Error('Cannot load without read() or XMLHttpRequest.'); + } + if (!success) ___setErrNo(ERRNO_CODES.EIO); + return success; + }, + // Creates a file record for lazy-loading from a URL. XXX This requires a synchronous + // XHR, which is not possible in browsers except in a web worker! Use preloading, + // either --preload-file in emcc or FS.createPreloadedFile + createLazyFile: function(parent, name, url, canRead, canWrite) { + if (typeof XMLHttpRequest !== 'undefined') { + if (!ENVIRONMENT_IS_WORKER) throw 'Cannot do synchronous binary XHRs outside webworkers in modern browsers. Use --embed-file or --preload-file in emcc'; + // Lazy chunked Uint8Array (implements get and length from Uint8Array). Actual getting is abstracted away for eventual reuse. + var LazyUint8Array = function() { + this.lengthKnown = false; + this.chunks = []; // Loaded chunks. Index is the chunk number + } + LazyUint8Array.prototype.get = function(idx) { + if (idx > this.length-1 || idx < 0) { + return undefined; + } + var chunkOffset = idx % this.chunkSize; + var chunkNum = Math.floor(idx / this.chunkSize); + return this.getter(chunkNum)[chunkOffset]; + } + LazyUint8Array.prototype.setDataGetter = function(getter) { + this.getter = getter; + } + LazyUint8Array.prototype.cacheLength = function() { + // Find length + var xhr = new XMLHttpRequest(); + xhr.open('HEAD', url, false); + xhr.send(null); + if (!(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304)) throw new Error("Couldn't load " + url + ". Status: " + xhr.status); + var datalength = Number(xhr.getResponseHeader("Content-length")); + var header; + var hasByteServing = (header = xhr.getResponseHeader("Accept-Ranges")) && header === "bytes"; +#if SMALL_XHR_CHUNKS + var chunkSize = 1024; // Chunk size in bytes +#else + var chunkSize = 1024*1024; // Chunk size in bytes +#endif + + if (!hasByteServing) chunkSize = datalength; + + // Function to get a range from the remote URL. + var doXHR = (function(from, to) { + if (from > to) throw new Error("invalid range (" + from + ", " + to + ") or no bytes requested!"); + if (to > datalength-1) throw new Error("only " + datalength + " bytes available! programmer error!"); + + // TODO: Use mozResponseArrayBuffer, responseStream, etc. if available. + var xhr = new XMLHttpRequest(); + xhr.open('GET', url, false); + if (datalength !== chunkSize) xhr.setRequestHeader("Range", "bytes=" + from + "-" + to); + + // Some hints to the browser that we want binary data. + if (typeof Uint8Array != 'undefined') xhr.responseType = 'arraybuffer'; + if (xhr.overrideMimeType) { + xhr.overrideMimeType('text/plain; charset=x-user-defined'); + } + + xhr.send(null); + if (!(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304)) throw new Error("Couldn't load " + url + ". Status: " + xhr.status); + if (xhr.response !== undefined) { + return new Uint8Array(xhr.response || []); + } else { + return intArrayFromString(xhr.responseText || '', true); + } + }); + var lazyArray = this; + lazyArray.setDataGetter(function(chunkNum) { + var start = chunkNum * chunkSize; + var end = (chunkNum+1) * chunkSize - 1; // including this byte + end = Math.min(end, datalength-1); // if datalength-1 is selected, this is the last block + if (typeof(lazyArray.chunks[chunkNum]) === "undefined") { + lazyArray.chunks[chunkNum] = doXHR(start, end); + } + if (typeof(lazyArray.chunks[chunkNum]) === "undefined") throw new Error("doXHR failed!"); + return lazyArray.chunks[chunkNum]; + }); + + this._length = datalength; + this._chunkSize = chunkSize; + this.lengthKnown = true; + } + + var lazyArray = new LazyUint8Array(); + Object.defineProperty(lazyArray, "length", { + get: function() { + if(!this.lengthKnown) { + this.cacheLength(); + } + return this._length; + } + }); + Object.defineProperty(lazyArray, "chunkSize", { + get: function() { + if(!this.lengthKnown) { + this.cacheLength(); + } + return this._chunkSize; + } + }); + + var properties = { isDevice: false, contents: lazyArray }; + } else { + var properties = { isDevice: false, url: url }; + } + + var node = FS.createFile(parent, name, properties, canRead, canWrite); + // This is a total hack, but I want to get this lazy file code out of the + // core of MEMFS. If we want to keep this lazy file concept I feel it should + // be its own thin LAZYFS proxying calls to MEMFS. + if (properties.contents) { + node.contents = properties.contents; + } else if (properties.url) { + node.contents = null; + node.url = properties.url; + } + // override each stream op with one that tries to force load the lazy file first + var stream_ops = {}; + var keys = Object.keys(node.stream_ops); + keys.forEach(function(key) { + var fn = node.stream_ops[key]; + stream_ops[key] = function() { + if (!FS.forceLoadFile(node)) { + throw new FS.ErrnoError(ERRNO_CODES.EIO); + } + return fn.apply(null, arguments); + }; + }); + // use a custom read function + stream_ops.read = function(stream, buffer, offset, length, position) { + var contents = stream.node.contents; + var size = Math.min(contents.length - position, length); + if (contents.slice) { // normal array + for (var i = 0; i < size; i++) { + buffer[offset + i] = contents[position + i]; + } + } else { + for (var i = 0; i < size; i++) { // LazyUint8Array from sync binary XHR + buffer[offset + i] = contents.get(position + i); + } + } + return size; + }; + node.stream_ops = stream_ops; + return node; + }, + // Preloads a file asynchronously. You can call this before run, for example in + // preRun. run will be delayed until this file arrives and is set up. + // If you call it after run(), you may want to pause the main loop until it + // completes, if so, you can use the onload parameter to be notified when + // that happens. + // In addition to normally creating the file, we also asynchronously preload + // the browser-friendly versions of it: For an image, we preload an Image + // element and for an audio, and Audio. These are necessary for SDL_Image + // and _Mixer to find the files in preloadedImages/Audios. + // You can also call this with a typed array instead of a url. It will then + // do preloading for the Image/Audio part, as if the typed array were the + // result of an XHR that you did manually. + createPreloadedFile: function(parent, name, url, canRead, canWrite, onload, onerror, dontCreateFile) { + Browser.init(); + // TODO we should allow people to just pass in a complete filename instead + // of parent and name being that we just join them anyways + var fullname = PATH.resolve(PATH.join(parent, name)); + function processData(byteArray) { + function finish(byteArray) { + if (!dontCreateFile) { + FS.createDataFile(parent, name, byteArray, canRead, canWrite); + } + if (onload) onload(); + removeRunDependency('cp ' + fullname); + } + var handled = false; + Module['preloadPlugins'].forEach(function(plugin) { + if (handled) return; + if (plugin['canHandle'](fullname)) { + plugin['handle'](byteArray, fullname, finish, function() { + if (onerror) onerror(); + removeRunDependency('cp ' + fullname); + }); + handled = true; + } + }); + if (!handled) finish(byteArray); + } + addRunDependency('cp ' + fullname); + if (typeof url == 'string') { + Browser.asyncLoad(url, function(byteArray) { + processData(byteArray); + }, onerror); + } else { + processData(url); + } + }, + + // + // general + // + createDefaultDirectories: function() { + FS.mkdir('/tmp', 0777); + }, + createDefaultDevices: function() { + // create /dev + FS.mkdir('/dev', 0777); + // setup /dev/null + FS.registerDevice(FS.makedev(1, 3), { + read: function() { return 0; }, + write: function() { return 0; } + }); + FS.mkdev('/dev/null', 0666, FS.makedev(1, 3)); + // setup /dev/tty and /dev/tty1 + // stderr needs to print output using Module['printErr'] + // so we register a second tty just for it. + TTY.register(FS.makedev(5, 0), TTY.default_tty_ops); + TTY.register(FS.makedev(6, 0), TTY.default_tty1_ops); + FS.mkdev('/dev/tty', 0666, FS.makedev(5, 0)); + FS.mkdev('/dev/tty1', 0666, FS.makedev(6, 0)); + // we're not going to emulate the actual shm device, + // just create the tmp dirs that reside in it commonly + FS.mkdir('/dev/shm', 0777); + FS.mkdir('/dev/shm/tmp', 0777); + }, + createStandardStreams: function() { + // TODO deprecate the old functionality of a single + // input / output callback and that utilizes FS.createDevice + // and instead require a unique set of stream ops + + // by default, we symlink the standard streams to the + // default tty devices. however, if the standard streams + // have been overwritten we create a unique device for + // them instead. + if (Module['stdin']) { + FS.createDevice('/dev', 'stdin', Module['stdin']); + } else { + FS.symlink('/dev/tty', '/dev/stdin'); + } + if (Module['stdout']) { + FS.createDevice('/dev', 'stdout', null, Module['stdout']); + } else { + FS.symlink('/dev/tty', '/dev/stdout'); + } + if (Module['stderr']) { + FS.createDevice('/dev', 'stderr', null, Module['stderr']); + } else { + FS.symlink('/dev/tty1', '/dev/stderr'); + } + + // open default streams for the stdin, stdout and stderr devices + var stdin = FS.open('/dev/stdin', 'r'); + {{{ makeSetValue(makeGlobalUse('_stdin'), 0, 'stdin.fd', 'void*') }}}; + assert(stdin.fd === 1, 'invalid handle for stdin (' + stdin.fd + ')'); + + var stdout = FS.open('/dev/stdout', 'w'); + {{{ makeSetValue(makeGlobalUse('_stdout'), 0, 'stdout.fd', 'void*') }}}; + assert(stdout.fd === 2, 'invalid handle for stdout (' + stdout.fd + ')'); + + var stderr = FS.open('/dev/stderr', 'w'); + {{{ makeSetValue(makeGlobalUse('_stderr'), 0, 'stderr.fd', 'void*') }}}; + assert(stderr.fd === 3, 'invalid handle for stderr (' + stderr.fd + ')'); + }, + staticInit: function() { + FS.root = FS.createNode(null, '/', {{{ cDefine('S_IFDIR') }}} | 0777, 0); + FS.mount(MEMFS, {}, '/'); + + FS.createDefaultDirectories(); + FS.createDefaultDevices(); + }, + init: function(input, output, error) { + assert(!FS.init.initialized, 'FS.init was previously called. If you want to initialize later with custom parameters, remove any earlier calls (note that one is automatically added to the generated code)'); + FS.init.initialized = true; + + // Allow Module.stdin etc. to provide defaults, if none explicitly passed to us here + Module['stdin'] = input || Module['stdin']; + Module['stdout'] = output || Module['stdout']; + Module['stderr'] = error || Module['stderr']; + + FS.createStandardStreams(); + }, + quit: function() { + FS.init.initialized = false; + for (var i = 0; i < FS.streams.length; i++) { + var stream = FS.streams[i]; + if (!stream) { + continue; + } + FS.close(stream); + } + }, + + // + // vfs functionality + // + mount: function(type, opts, mountpoint) { + var mount = { + type: type, + opts: opts, + mountpoint: mountpoint, + root: null + }; + var lookup; + if (mountpoint) { + lookup = FS.lookupPath(mountpoint, { follow: false }); + } + // create a root node for the fs + var root = type.mount(mount); + root.mount = mount; + mount.root = root; + // assign the mount info to the mountpoint's node + if (lookup) { + lookup.node.mount = mount; + lookup.node.mounted = true; + // compatibility update FS.root if we mount to / + if (mountpoint === '/') { + FS.root = mount.root; + } + } + return root; + }, + lookup: function(parent, name) { + return parent.node_ops.lookup(parent, name); + }, + // generic function for all node creation + mknod: function(path, mode, dev) { + var lookup = FS.lookupPath(path, { parent: true }); + var parent = lookup.node; + var name = PATH.basename(path); + var err = FS.mayCreate(parent, name); + if (err) { + throw new FS.ErrnoError(err); + } + if (!parent.node_ops.mknod) { + throw new FS.ErrnoError(ERRNO_CODES.EPERM); + } + return parent.node_ops.mknod(parent, name, mode, dev); + }, + // helpers to create specific types of nodes + create: function(path, mode) { + mode &= {{{ cDefine('S_IALLUGO') }}}; + mode |= {{{ cDefine('S_IFREG') }}}; + return FS.mknod(path, mode, 0); + }, + mkdir: function(path, mode) { + mode &= {{{ cDefine('S_IRWXUGO') }}} | {{{ cDefine('S_ISVTX') }}}; + mode |= {{{ cDefine('S_IFDIR') }}}; + return FS.mknod(path, mode, 0); + }, + mkdev: function(path, mode, dev) { + mode |= {{{ cDefine('S_IFCHR') }}}; + return FS.mknod(path, mode, dev); + }, + symlink: function(oldpath, newpath) { + var lookup = FS.lookupPath(newpath, { parent: true }); + var parent = lookup.node; + var newname = PATH.basename(newpath); + var err = FS.mayCreate(parent, newname); + if (err) { + throw new FS.ErrnoError(err); + } + if (!parent.node_ops.symlink) { + throw new FS.ErrnoError(ERRNO_CODES.EPERM); + } + return parent.node_ops.symlink(parent, newname, oldpath); + }, + rename: function(old_path, new_path) { + var old_dirname = PATH.dirname(old_path); + var new_dirname = PATH.dirname(new_path); + var old_name = PATH.basename(old_path); + var new_name = PATH.basename(new_path); + // parents must exist + var lookup, old_dir, new_dir; + try { + lookup = FS.lookupPath(old_path, { parent: true }); + old_dir = lookup.node; + lookup = FS.lookupPath(new_path, { parent: true }); + new_dir = lookup.node; + } catch (e) { + throw new FS.ErrnoError(ERRNO_CODES.EBUSY); + } + // need to be part of the same mount + if (old_dir.mount !== new_dir.mount) { + throw new FS.ErrnoError(ERRNO_CODES.EXDEV); + } + // source must exist + var old_node = FS.lookupNode(old_dir, old_name); + // old path should not be an ancestor of the new path + var relative = PATH.relative(old_path, new_dirname); + if (relative.charAt(0) !== '.') { + throw new FS.ErrnoError(ERRNO_CODES.EINVAL); + } + // new path should not be an ancestor of the old path + relative = PATH.relative(new_path, old_dirname); + if (relative.charAt(0) !== '.') { + throw new FS.ErrnoError(ERRNO_CODES.ENOTEMPTY); + } + // see if the new path already exists + var new_node; + try { + new_node = FS.lookupNode(new_dir, new_name); + } catch (e) { + // not fatal + } + // early out if nothing needs to change + if (old_node === new_node) { + return; + } + // we'll need to delete the old entry + var isdir = FS.isDir(old_node.mode); + var err = FS.mayDelete(old_dir, old_name, isdir); + if (err) { + throw new FS.ErrnoError(err); + } + // need delete permissions if we'll be overwriting. + // need create permissions if new doesn't already exist. + err = new_node ? + FS.mayDelete(new_dir, new_name, isdir) : + FS.mayCreate(new_dir, new_name); + if (err) { + throw new FS.ErrnoError(err); + } + if (!old_dir.node_ops.rename) { + throw new FS.ErrnoError(ERRNO_CODES.EPERM); + } + if (FS.isMountpoint(old_node) || (new_node && FS.isMountpoint(new_node))) { + throw new FS.ErrnoError(ERRNO_CODES.EBUSY); + } + // if we are going to change the parent, check write permissions + if (new_dir !== old_dir) { + err = FS.nodePermissions(old_dir, 'w'); + if (err) { + throw new FS.ErrnoError(err); + } + } + // remove the node from the lookup hash + FS.hashRemoveNode(old_node); + // do the underlying fs rename + try { + old_node.node_ops.rename(old_node, new_dir, new_name); + } catch (e) { + throw e; + } finally { + // add the node back to the hash (in case node_ops.rename + // changed its name) + FS.hashAddNode(old_node); + } + }, + rmdir: function(path) { + var lookup = FS.lookupPath(path, { parent: true }); + var parent = lookup.node; + var name = PATH.basename(path); + var node = FS.lookupNode(parent, name); + var err = FS.mayDelete(parent, name, true); + if (err) { + throw new FS.ErrnoError(err); + } + if (!parent.node_ops.rmdir) { + throw new FS.ErrnoError(ERRNO_CODES.EPERM); + } + if (FS.isMountpoint(node)) { + throw new FS.ErrnoError(ERRNO_CODES.EBUSY); + } + parent.node_ops.rmdir(parent, name); + FS.destroyNode(node); + }, + unlink: function(path) { + var lookup = FS.lookupPath(path, { parent: true }); + var parent = lookup.node; + var name = PATH.basename(path); + var node = FS.lookupNode(parent, name); + var err = FS.mayDelete(parent, name, false); + if (err) { + // POSIX says unlink should set EPERM, not EISDIR + if (err === ERRNO_CODES.EISDIR) err = ERRNO_CODES.EPERM; + throw new FS.ErrnoError(err); + } + if (!parent.node_ops.unlink) { + throw new FS.ErrnoError(ERRNO_CODES.EPERM); + } + if (FS.isMountpoint(node)) { + throw new FS.ErrnoError(ERRNO_CODES.EBUSY); + } + parent.node_ops.unlink(parent, name); + FS.destroyNode(node); + }, + readlink: function(path) { + var lookup = FS.lookupPath(path, { follow: false }); + var link = lookup.node; + if (!link.node_ops.readlink) { + throw new FS.ErrnoError(ERRNO_CODES.EINVAL); + } + return link.node_ops.readlink(link); + }, + stat: function(path, dontFollow) { + var lookup = FS.lookupPath(path, { follow: !dontFollow }); + var node = lookup.node; + if (!node.node_ops.getattr) { + throw new FS.ErrnoError(ERRNO_CODES.EPERM); + } + return node.node_ops.getattr(node); + }, + lstat: function(path) { + return FS.stat(path, true); + }, + chmod: function(path, mode, dontFollow) { + var node; + if (typeof path === 'string') { + var lookup = FS.lookupPath(path, { follow: !dontFollow }); + node = lookup.node; + } else { + node = path; + } + if (!node.node_ops.setattr) { + throw new FS.ErrnoError(ERRNO_CODES.EPERM); + } + node.node_ops.setattr(node, { + mode: (mode & {{{ cDefine('S_IALLUGO') }}}) | (node.mode & ~{{{ cDefine('S_IALLUGO') }}}), + timestamp: Date.now() + }); + }, + lchmod: function(path, mode) { + FS.chmod(path, mode, true); + }, + fchmod: function(fd, mode) { + var stream = FS.getStream(fd); + if (!stream) { + throw new FS.ErrnoError(ERRNO_CODES.EBADF); + } + FS.chmod(stream.node, mode); + }, + chown: function(path, uid, gid, dontFollow) { + var node; + if (typeof path === 'string') { + var lookup = FS.lookupPath(path, { follow: !dontFollow }); + node = lookup.node; + } else { + node = path; + } + if (!node.node_ops.setattr) { + throw new FS.ErrnoError(ERRNO_CODES.EPERM); + } + node.node_ops.setattr(node, { + timestamp: Date.now() + // we ignore the uid / gid for now + }); + }, + lchown: function(path, uid, gid) { + FS.chown(path, uid, gid, true); + }, + fchown: function(fd, uid, gid) { + var stream = FS.getStream(fd); + if (!stream) { + throw new FS.ErrnoError(ERRNO_CODES.EBADF); + } + FS.chown(stream.node, uid, gid); + }, + truncate: function(path, len) { + if (len < 0) { + throw new FS.ErrnoError(ERRNO_CODES.EINVAL); + } + var node; + if (typeof path === 'string') { + var lookup = FS.lookupPath(path, { follow: true }); + node = lookup.node; + } else { + node = path; + } + if (!node.node_ops.setattr) { + throw new FS.ErrnoError(ERRNO_CODES.EPERM); + } + if (FS.isDir(node.mode)) { + throw new FS.ErrnoError(ERRNO_CODES.EISDIR); + } + if (!FS.isFile(node.mode)) { + throw new FS.ErrnoError(ERRNO_CODES.EINVAL); + } + var err = FS.nodePermissions(node, 'w'); + if (err) { + throw new FS.ErrnoError(err); + } + node.node_ops.setattr(node, { + size: len, + timestamp: Date.now() + }); + }, + ftruncate: function(fd, len) { + var stream = FS.getStream(fd); + if (!stream) { + throw new FS.ErrnoError(ERRNO_CODES.EBADF); + } + if ((stream.flags & {{{ cDefine('O_ACCMODE') }}}) === {{{ cDefine('O_RDONLY')}}}) { + throw new FS.ErrnoError(ERRNO_CODES.EINVAL); + } + FS.truncate(stream.node, len); + }, + utime: function(path, atime, mtime) { + var lookup = FS.lookupPath(path, { follow: true }); + var node = lookup.node; + node.node_ops.setattr(node, { + timestamp: Math.max(atime, mtime) + }); + }, + open: function(path, flags, mode, fd_start, fd_end) { + path = PATH.normalize(path); + flags = typeof flags === 'string' ? FS.modeStringToFlags(flags) : flags; + if ((flags & {{{ cDefine('O_CREAT') }}})) { + mode = (mode & {{{ cDefine('S_IALLUGO') }}}) | {{{ cDefine('S_IFREG') }}}; + } else { + mode = 0; + } + var node; + try { + var lookup = FS.lookupPath(path, { + follow: !(flags & {{{ cDefine('O_NOFOLLOW') }}}) + }); + node = lookup.node; + path = lookup.path; + } catch (e) { + // ignore + } + // perhaps we need to create the node + if ((flags & {{{ cDefine('O_CREAT') }}})) { + if (node) { + // if O_CREAT and O_EXCL are set, error out if the node already exists + if ((flags & {{{ cDefine('O_EXCL') }}})) { + throw new FS.ErrnoError(ERRNO_CODES.EEXIST); + } + } else { + // node doesn't exist, try to create it + node = FS.mknod(path, mode, 0); + } + } + if (!node) { + throw new FS.ErrnoError(ERRNO_CODES.ENOENT); + } + // can't truncate a device + if (FS.isChrdev(node.mode)) { + flags &= ~{{{ cDefine('O_TRUNC') }}}; + } + // check permissions + var err = FS.mayOpen(node, flags); + if (err) { + throw new FS.ErrnoError(err); + } + // do truncation if necessary + if ((flags & {{{ cDefine('O_TRUNC')}}})) { + FS.truncate(node, 0); + } + // register the stream with the filesystem + var stream = FS.createStream({ + path: path, + node: node, + flags: flags, + seekable: true, + position: 0, + stream_ops: node.stream_ops, + // used by the file family libc calls (fopen, fwrite, ferror, etc.) + ungotten: [], + error: false + }, fd_start, fd_end); + // call the new stream's open function + if (stream.stream_ops.open) { + stream.stream_ops.open(stream); + } + return stream; + }, + close: function(stream) { + try { + if (stream.stream_ops.close) { + stream.stream_ops.close(stream); + } + } catch (e) { + throw e; + } finally { + FS.closeStream(stream.fd); + } + }, + llseek: function(stream, offset, whence) { + if (!stream.seekable || !stream.stream_ops.llseek) { + throw new FS.ErrnoError(ERRNO_CODES.ESPIPE); + } + return stream.stream_ops.llseek(stream, offset, whence); + }, + readdir: function(stream) { + if (!stream.stream_ops.readdir) { + throw new FS.ErrnoError(ERRNO_CODES.ENOTDIR); + } + return stream.stream_ops.readdir(stream); + }, + read: function(stream, buffer, offset, length, position) { + if (length < 0 || position < 0) { + throw new FS.ErrnoError(ERRNO_CODES.EINVAL); + } + if ((stream.flags & {{{ cDefine('O_ACCMODE') }}}) === {{{ cDefine('O_WRONLY')}}}) { + throw new FS.ErrnoError(ERRNO_CODES.EBADF); + } + if (FS.isDir(stream.node.mode)) { + throw new FS.ErrnoError(ERRNO_CODES.EISDIR); + } + if (!stream.stream_ops.read) { + throw new FS.ErrnoError(ERRNO_CODES.EINVAL); + } + var seeking = true; + if (typeof position === 'undefined') { + position = stream.position; + seeking = false; + } else if (!stream.seekable) { + throw new FS.ErrnoError(ERRNO_CODES.ESPIPE); + } + var bytesRead = stream.stream_ops.read(stream, buffer, offset, length, position); + if (!seeking) stream.position += bytesRead; + return bytesRead; + }, + write: function(stream, buffer, offset, length, position) { + if (length < 0 || position < 0) { + throw new FS.ErrnoError(ERRNO_CODES.EINVAL); + } + if ((stream.flags & {{{ cDefine('O_ACCMODE') }}}) === {{{ cDefine('O_RDONLY')}}}) { + throw new FS.ErrnoError(ERRNO_CODES.EBADF); + } + if (FS.isDir(stream.node.mode)) { + throw new FS.ErrnoError(ERRNO_CODES.EISDIR); + } + if (!stream.stream_ops.write) { + throw new FS.ErrnoError(ERRNO_CODES.EINVAL); + } + var seeking = true; + if (typeof position === 'undefined') { + position = stream.position; + seeking = false; + } else if (!stream.seekable) { + throw new FS.ErrnoError(ERRNO_CODES.ESPIPE); + } + if (stream.flags & {{{ cDefine('O_APPEND') }}}) { + // seek to the end before writing in append mode + FS.llseek(stream, 0, {{{ cDefine('SEEK_END') }}}); + } + var bytesWritten = stream.stream_ops.write(stream, buffer, offset, length, position); + if (!seeking) stream.position += bytesWritten; + return bytesWritten; + }, + allocate: function(stream, offset, length) { + if (offset < 0 || length <= 0) { + throw new FS.ErrnoError(ERRNO_CODES.EINVAL); + } + if ((stream.flags & {{{ cDefine('O_ACCMODE') }}}) === {{{ cDefine('O_RDONLY')}}}) { + throw new FS.ErrnoError(ERRNO_CODES.EBADF); + } + if (!FS.isFile(stream.node.mode) && !FS.isDir(node.mode)) { + throw new FS.ErrnoError(ERRNO_CODES.ENODEV); + } + if (!stream.stream_ops.allocate) { + throw new FS.ErrnoError(ERRNO_CODES.EOPNOTSUPP); + } + stream.stream_ops.allocate(stream, offset, length); + }, + mmap: function(stream, buffer, offset, length, position, prot, flags) { + // TODO if PROT is PROT_WRITE, make sure we have write access + if ((stream.flags & {{{ cDefine('O_ACCMODE') }}}) === {{{ cDefine('O_WRONLY')}}}) { + throw new FS.ErrnoError(ERRNO_CODES.EACCES); + } + if (!stream.stream_ops.mmap) { + throw new FS.errnoError(ERRNO_CODES.ENODEV); + } + return stream.stream_ops.mmap(stream, buffer, offset, length, position, prot, flags); + } + } +}); \ No newline at end of file diff --git a/src/library_memfs.js b/src/library_memfs.js new file mode 100644 index 0000000000000..a044e0c6f723e --- /dev/null +++ b/src/library_memfs.js @@ -0,0 +1,217 @@ +mergeInto(LibraryManager.library, { + $MEMFS__deps: ['$FS'], + $MEMFS: { + mount: function(mount) { + return MEMFS.create_node(null, '/', {{{ cDefine('S_IFDIR') }}} | 0777, 0); + }, + create_node: function(parent, name, mode, dev) { + if (FS.isBlkdev(mode) || FS.isFIFO(mode)) { + // no supported + throw new FS.ErrnoError(ERRNO_CODES.EPERM); + } + var node = FS.createNode(parent, name, mode, dev); + node.node_ops = MEMFS.node_ops; + if (FS.isDir(node.mode)) { + node.stream_ops = MEMFS.stream_ops; + node.contents = {}; + } else if (FS.isFile(node.mode)) { + node.stream_ops = MEMFS.stream_ops; + node.contents = []; + } else if (FS.isLink(node.mode)) { + node.stream_ops = MEMFS.stream_ops; + } else if (FS.isChrdev(node.mode)) { + node.stream_ops = FS.chrdev_stream_ops; + } + node.timestamp = Date.now(); + // add the new node to the parent + if (parent) { + parent.contents[name] = node; + } + return node; + }, + node_ops: { + getattr: function(node) { + var attr = {}; + // device numbers reuse inode numbers. + attr.dev = FS.isChrdev(node.mode) ? node.id : 1; + attr.ino = node.id; + attr.mode = node.mode; + attr.nlink = 1; + attr.uid = 0; + attr.gid = 0; + attr.rdev = node.rdev; + if (FS.isDir(node.mode)) { + attr.size = 4096; + } else if (FS.isFile(node.mode)) { + attr.size = node.contents.length; + } else if (FS.isLink(node.mode)) { + attr.size = node.link.length; + } else { + attr.size = 0; + } + attr.atime = new Date(node.timestamp); + attr.mtime = new Date(node.timestamp); + attr.ctime = new Date(node.timestamp); + // NOTE: In our implementation, st_blocks = Math.ceil(st_size/st_blksize), + // but this is not required by the standard. + attr.blksize = 4096; + attr.blocks = Math.ceil(attr.size / attr.blksize); + return attr; + }, + setattr: function(node, attr) { + if (attr.mode !== undefined) { + node.mode = attr.mode; + } + if (attr.timestamp !== undefined) { + node.timestamp = attr.timestamp; + } + if (attr.size !== undefined) { + var contents = node.contents; + if (attr.size < contents.length) contents.length = attr.size; + else while (attr.size > contents.length) contents.push(0); + } + }, + lookup: function(parent, name) { + throw new FS.ErrnoError(ERRNO_CODES.ENOENT); + }, + mknod: function(parent, name, mode, dev) { + return MEMFS.create_node(parent, name, mode, dev); + }, + rename: function(old_node, new_dir, new_name) { + // if we're overwriting a directory at new_name, make sure it's empty. + if (FS.isDir(old_node.mode)) { + var new_node; + try { + new_node = FS.lookupNode(new_dir, new_name); + } catch (e) { + } + if (new_node) { + for (var i in new_node.contents) { + throw new FS.ErrnoError(ERRNO_CODES.ENOTEMPTY); + } + } + } + // do the internal rewiring + delete old_node.parent.contents[old_node.name]; + old_node.name = new_name; + new_dir.contents[new_name] = old_node; + }, + unlink: function(parent, name) { + delete parent.contents[name]; + }, + rmdir: function(parent, name) { + var node = FS.lookupNode(parent, name); + for (var i in node.contents) { + throw new FS.ErrnoError(ERRNO_CODES.ENOTEMPTY); + } + delete parent.contents[name]; + }, + symlink: function(parent, newname, oldpath) { + var node = MEMFS.create_node(parent, newname, 0777 | {{{ cDefine('S_IFLNK') }}}, 0); + node.link = oldpath; + return node; + }, + readlink: function(node) { + if (!FS.isLink(node.mode)) { + throw new FS.ErrnoError(ERRNO_CODES.EINVAL); + } + return node.link; + }, + }, + stream_ops: { + open: function(stream) { + if (FS.isDir(stream.node.mode)) { + // cache off the directory entries when open'd + var entries = ['.', '..'] + for (var key in stream.node.contents) { + if (!stream.node.contents.hasOwnProperty(key)) { + continue; + } + entries.push(key); + } + stream.entries = entries; + } + }, + read: function(stream, buffer, offset, length, position) { + var contents = stream.node.contents; + var size = Math.min(contents.length - position, length); +#if USE_TYPED_ARRAYS == 2 + if (contents.subarray) { // typed array + buffer.set(contents.subarray(position, position + size), offset); + } else +#endif + { + for (var i = 0; i < size; i++) { + buffer[offset + i] = contents[position + i]; + } + } + return size; + }, + write: function(stream, buffer, offset, length, position) { + var contents = stream.node.contents; + while (contents.length < position) contents.push(0); + for (var i = 0; i < length; i++) { + contents[position + i] = buffer[offset + i]; + } + stream.node.timestamp = Date.now(); + return length; + }, + llseek: function(stream, offset, whence) { + var position = offset; + if (whence === 1) { // SEEK_CUR. + position += stream.position; + } else if (whence === 2) { // SEEK_END. + if (FS.isFile(stream.node.mode)) { + position += stream.node.contents.length; + } + } + if (position < 0) { + throw new FS.ErrnoError(ERRNO_CODES.EINVAL); + } + stream.ungotten = []; + stream.position = position; + return position; + }, + readdir: function(stream) { + return stream.entries; + }, + allocate: function(stream, offset, length) { + var contents = stream.node.contents; + var limit = offset + length; + while (limit > contents.length) contents.push(0); + }, + mmap: function(stream, buffer, offset, length, position, prot, flags) { + if (!FS.isFile(stream.node.mode)) { + throw new FS.ErrnoError(ERRNO_CODES.ENODEV); + } + var ptr; + var allocated; + var contents = stream.node.contents; + // Only make a new copy when MAP_PRIVATE is specified. + if (!(flags & {{{ cDefine('MAP_PRIVATE') }}})) { + // We can't emulate MAP_SHARED when the file is not backed by the buffer + // we're mapping to (e.g. the HEAP buffer). + assert(contents.buffer === buffer || contents.buffer === buffer.buffer); + allocated = false; + ptr = contents.byteOffset; + } else { + // Try to avoid unnecessary slices. + if (position > 0 || position + length < contents.length) { + if (contents.subarray) { + contents = contents.subarray(position, position + length); + } else { + contents = Array.prototype.slice.call(contents, position, position + length); + } + } + allocated = true; + ptr = _malloc(length); + if (!ptr) { + throw new FS.ErrnoError(ERRNO_CODES.ENOMEM); + } + buffer.set(contents, ptr); + } + return { ptr: ptr, allocated: allocated }; + }, + } + } +}); \ No newline at end of file diff --git a/src/library_sockfs.js b/src/library_sockfs.js new file mode 100644 index 0000000000000..13118b71fb37f --- /dev/null +++ b/src/library_sockfs.js @@ -0,0 +1,18 @@ +mergeInto(LibraryManager.library, { + $SOCKFS__postset: '__ATINIT__.push({ func: function() { SOCKFS.root = FS.mount(SOCKFS, {}, null); } });', + $SOCKFS__deps: ['$FS'], + $SOCKFS: { + mount: function(mount) { + var node = FS.createNode(null, '/', {{{ cDefine('S_IFDIR') }}} | 0777, 0); + node.node_ops = SOCKFS.node_ops; + node.stream_ops = SOCKFS.stream_ops; + return node; + }, + node_ops: { + }, + stream_ops: { + }, + websocket_sock_ops: { + } + } +}); \ No newline at end of file diff --git a/src/library_tty.js b/src/library_tty.js new file mode 100644 index 0000000000000..8f44cd074ba0c --- /dev/null +++ b/src/library_tty.js @@ -0,0 +1,121 @@ +mergeInto(LibraryManager.library, { + $TTY__deps: ['$FS'], + $TTY: { + ttys: [], + register: function(dev, ops) { + TTY.ttys[dev] = { input: [], output: [], ops: ops }; + FS.registerDevice(dev, TTY.stream_ops); + }, + stream_ops: { + open: function(stream) { + // this wouldn't be required if the library wasn't eval'd at first... + if (!TTY.utf8) { + TTY.utf8 = new Runtime.UTF8Processor(); + } + var tty = TTY.ttys[stream.node.rdev]; + if (!tty) { + throw new FS.ErrnoError(ERRNO_CODES.ENODEV); + } + stream.tty = tty; + stream.seekable = false; + }, + close: function(stream) { + // flush any pending line data + if (stream.tty.output.length) { + stream.tty.ops.put_char(stream.tty, {{{ charCode('\n') }}}); + } + }, + read: function(stream, buffer, offset, length, pos /* ignored */) { + if (!stream.tty || !stream.tty.ops.get_char) { + throw new FS.ErrnoError(ERRNO_CODES.ENXIO); + } + var bytesRead = 0; + for (var i = 0; i < length; i++) { + var result; + try { + result = stream.tty.ops.get_char(stream.tty); + } catch (e) { + throw new FS.ErrnoError(ERRNO_CODES.EIO); + } + if (result === undefined && bytesRead === 0) { + throw new FS.ErrnoError(ERRNO_CODES.EAGAIN); + } + if (result === null || result === undefined) break; + bytesRead++; + buffer[offset+i] = result; + } + if (bytesRead) { + stream.node.timestamp = Date.now(); + } + return bytesRead; + }, + write: function(stream, buffer, offset, length, pos) { + if (!stream.tty || !stream.tty.ops.put_char) { + throw new FS.ErrnoError(ERRNO_CODES.ENXIO); + } + for (var i = 0; i < length; i++) { + try { + stream.tty.ops.put_char(stream.tty, buffer[offset+i]); + } catch (e) { + throw new FS.ErrnoError(ERRNO_CODES.EIO); + } + } + if (length) { + stream.node.timestamp = Date.now(); + } + return i; + } + }, + // NOTE: This is weird to support stdout and stderr + // overrides in addition to print and printErr overrides. + default_tty_ops: { + get_char: function(tty) { + if (!tty.input.length) { + var result = null; + if (ENVIRONMENT_IS_NODE) { + if (process.stdin.destroyed) { + return undefined; + } + result = process.stdin.read(); + } else if (typeof window != 'undefined' && + typeof window.prompt == 'function') { + // Browser. + result = window.prompt('Input: '); // returns null on cancel + if (result !== null) { + result += '\n'; + } + } else if (typeof readline == 'function') { + // Command line. + result = readline(); + if (result !== null) { + result += '\n'; + } + } + if (!result) { + return null; + } + tty.input = intArrayFromString(result, true); + } + return tty.input.shift(); + }, + put_char: function(tty, val) { + if (val === null || val === {{{ charCode('\n') }}}) { + Module['print'](tty.output.join('')); + tty.output = []; + } else { + tty.output.push(TTY.utf8.processCChar(val)); + } + } + }, + default_tty1_ops: { + put_char: function(tty, val) { + if (val === null || val === {{{ charCode('\n') }}}) { + Module['printErr'](tty.output.join('')); + tty.output = []; + } else { + tty.output.push(TTY.utf8.processCChar(val)); + } + } + } + } +}); \ No newline at end of file diff --git a/src/modules.js b/src/modules.js index edfbae5c68c86..135617772f8ba 100644 --- a/src/modules.js +++ b/src/modules.js @@ -424,7 +424,7 @@ var LibraryManager = { load: function() { if (this.library) return; - var libraries = ['library.js', 'library_path.js', 'library_browser.js', 'library_sdl.js', 'library_gl.js', 'library_glut.js', 'library_xlib.js', 'library_egl.js', 'library_gc.js', 'library_jansson.js', 'library_openal.js', 'library_glfw.js'].concat(additionalLibraries); + var libraries = ['library.js', 'library_path.js', 'library_fs.js', 'library_memfs.js', 'library_sockfs.js', 'library_tty.js', 'library_browser.js', 'library_sdl.js', 'library_gl.js', 'library_glut.js', 'library_xlib.js', 'library_egl.js', 'library_gc.js', 'library_jansson.js', 'library_openal.js', 'library_glfw.js'].concat(additionalLibraries); for (var i = 0; i < libraries.length; i++) { eval(processMacros(preprocess(read(libraries[i])))); } From f18f02edef06fcdd7b1dcca7017e563d82a9f4c3 Mon Sep 17 00:00:00 2001 From: Anthony Pesch Date: Sat, 10 Aug 2013 17:05:22 -0700 Subject: [PATCH 034/112] updated paths to be compatible on non-unix environments --- tests/runner.py | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/tests/runner.py b/tests/runner.py index 3a8e87512ba00..05f7e97c43c52 100755 --- a/tests/runner.py +++ b/tests/runner.py @@ -14035,7 +14035,7 @@ def __enter__(self): # the current test_enet if self.filename: Popen([CLANG_CC, path_from_root('tests', self.filename), '-o', 'server'] + self.args).communicate() - process = Popen(['./server']) + process = Popen([os.path.abspath('server')]) self.pids.append(process.pid) # start the websocket proxy @@ -14076,7 +14076,7 @@ def __exit__(self, *args, **kwargs): def make_relay_server(self, port1, port2): print >> sys.stderr, 'creating relay server on ports %d,%d' % (port1, port2) - proc = Popen([PYTHON, path_from_root('tests', 'sockets/socket_relay.py'), str(port1), str(port2)]) + proc = Popen([PYTHON, path_from_root('tests', 'sockets', 'socket_relay.py'), str(port1), str(port2)]) return proc # always run these tests last @@ -14087,20 +14087,20 @@ def make_relay_server(self, port1, port2): # proper listen server support. def test_sockets_echo(self): - sockets_include = '-I'+path_from_root('tests/sockets') + sockets_include = '-I'+path_from_root('tests', 'sockets') for datagram in [0]: dgram_define = '-DTEST_DGRAM=%d' % datagram for harness in [ - self.WebsockifyServerHarness('sockets/test_sockets_echo_server.c', ['-DSOCKK=8990', dgram_define, sockets_include], 8991, 8990) - # self.CompiledServerHarness('sockets/test_sockets_echo_server.c', ['-DSOCKK=8990', dgram_define, sockets_include]) + self.WebsockifyServerHarness(os.path.join('sockets', 'test_sockets_echo_server.c'), ['-DSOCKK=8990', dgram_define, sockets_include], 8991, 8990) + # self.CompiledServerHarness(os.path.join('sockets', 'test_sockets_echo_server.c'), ['-DSOCKK=8990', dgram_define, sockets_include]) ]: with harness: - self.btest('sockets/test_sockets_echo_client.c', expected='0', args=['-DSOCKK=8991', dgram_define, sockets_include]) + self.btest(os.path.join('sockets', 'test_sockets_echo_client.c'), expected='0', args=['-DSOCKK=8991', dgram_define, sockets_include]) def test_sockets_echo_bigdata(self): - sockets_include = '-I'+path_from_root('tests/sockets') + sockets_include = '-I'+path_from_root('tests', 'sockets') for datagram in [0]: dgram_define = '-DTEST_DGRAM=%d' % datagram @@ -14111,41 +14111,41 @@ def test_sockets_echo_bigdata(self): message += str(unichr(ord('a') + (i % 26))) # re-write the client test with this literal (it's too big to pass via command line) - input_filename = path_from_root('tests/sockets', 'test_sockets_echo_client.c') + input_filename = path_from_root('tests', 'sockets', 'test_sockets_echo_client.c') input = open(input_filename).read() output = input.replace('#define MESSAGE "pingtothepong"', '#define MESSAGE "%s"' % message) for harness in [ - self.WebsockifyServerHarness('sockets/test_sockets_echo_server.c', ['-DSOCKK=8992', dgram_define, sockets_include], 8993, 8992) + self.WebsockifyServerHarness(os.path.join('sockets', 'test_sockets_echo_server.c'), ['-DSOCKK=8992', dgram_define, sockets_include], 8993, 8992) ]: with harness: self.btest(output, expected='0', args=['-DSOCKK=8993', dgram_define, sockets_include], force_c=True) def test_sockets_partial(self): for harness in [ - self.WebsockifyServerHarness('sockets/test_sockets_partial_server.c', ['-DSOCKK=8994'], 8995, 8994) + self.WebsockifyServerHarness(os.path.join('sockets', 'test_sockets_partial_server.c'), ['-DSOCKK=8994'], 8995, 8994) ]: with harness: - self.btest('sockets/test_sockets_partial_client.c', expected='165', args=['-DSOCKK=8995']) + self.btest(os.path.join('sockets', 'test_sockets_partial_client.c'), expected='165', args=['-DSOCKK=8995']) # TODO add support for gethostbyaddr to re-enable this test # def test_sockets_gethostbyname(self): - # self.btest('sockets/test_sockets_gethostbyname.c', expected='0', args=['-O2', '-DSOCKK=8997']) + # self.btest(os.path.join('sockets', 'test_sockets_gethostbyname.c'), expected='0', args=['-O2', '-DSOCKK=8997']) def test_sockets_select_server_no_accept(self): for harness in [ - self.WebsockifyServerHarness('sockets/test_sockets_select_server_no_accept_server.c', ['-DSOCKK=8995'], 8996, 8995) + self.WebsockifyServerHarness(os.path.join('sockets', 'test_sockets_select_server_no_accept_server.c'), ['-DSOCKK=8995'], 8996, 8995) ]: - self.btest('sockets/test_sockets_select_server_no_accept_client.c', expected='266', args=['-DSOCKK=8996']) + self.btest(os.path.join('sockets', 'test_sockets_select_server_no_accept_client.c'), expected='266', args=['-DSOCKK=8996']) def test_sockets_select_server_closes_connection_rw(self): - sockets_include = '-I'+path_from_root('tests/sockets') + sockets_include = '-I'+path_from_root('tests', 'sockets') for harness in [ - self.WebsockifyServerHarness('sockets/test_sockets_echo_server.c', ['-DSOCKK=9004', sockets_include], 9005, 9004) + self.WebsockifyServerHarness(os.path.join('sockets', 'test_sockets_echo_server.c'), ['-DSOCKK=9004', sockets_include], 9005, 9004) ]: with harness: - self.btest('sockets/test_sockets_select_server_closes_connection_client_rw.c', expected='266', args=['-DSOCKK=9005', sockets_include]) + self.btest(os.path.join('sockets', 'test_sockets_select_server_closes_connection_client_rw.c'), expected='266', args=['-DSOCKK=9005', sockets_include]) # TODO remove this once we have proper listen server support built into emscripten. # being that enet uses datagram sockets, we can't proxy to a native server with @@ -14160,7 +14160,7 @@ def test_enet(self): Popen([PYTHON, path_from_root('emmake'), 'make']).communicate() enet = [self.in_dir('enet', '.libs', 'libenet.a'), '-I'+path_from_root('tests', 'enet', 'include')] os.chdir(pwd) - Popen([PYTHON, EMCC, path_from_root('tests/sockets', 'test_enet_server.c'), '-o', 'server.html', '-DSOCKK=2235'] + enet).communicate() + Popen([PYTHON, EMCC, path_from_root('tests', 'sockets', 'test_enet_server.c'), '-o', 'server.html', '-DSOCKK=2235'] + enet).communicate() with self.WebsockifyServerHarness('', [], 2235, 2234): with self.WebsockifyServerHarness('', [], 2237, 2236): @@ -14168,7 +14168,7 @@ def test_enet(self): try: proc = self.make_relay_server(2234, 2236) pids.append(proc.pid) - self.btest('sockets/test_enet_client.c', expected='0', args=['-DSOCKK=2237'] + enet) + self.btest(os.path.join('sockets', 'test_enet_client.c'), expected='0', args=['-DSOCKK=2237'] + enet) finally: browser.clean_pids(pids); @@ -14195,10 +14195,10 @@ def test_enet(self): # os.chdir(pwd) # for harness in [ - # self.CompiledServerHarness('sockets/test_enet_server.c', ['-DSOCKK=9010'] + enet_native, 9011, 9010) + # self.CompiledServerHarness(os.path.join('sockets', 'test_enet_server.c'), ['-DSOCKK=9010'] + enet_native, 9011, 9010) # ]: # with harness: - # self.btest('sockets/test_enet_client.c', expected='0', args=['-DSOCKK=9011'] + enet_emscripten) + # self.btest(os.path.join('sockets', 'test_enet_client.c'), expected='0', args=['-DSOCKK=9011'] + enet_emscripten) elif 'benchmark' in str(sys.argv): # Benchmarks. Run them with argument |benchmark|. To run a specific test, do From 1cac914671205affad037b6777378d876cf6e36a Mon Sep 17 00:00:00 2001 From: Anthony Pesch Date: Sat, 10 Aug 2013 17:19:52 -0700 Subject: [PATCH 035/112] removed bad comments --- tests/runner.py | 23 ++++++----------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/tests/runner.py b/tests/runner.py index 05f7e97c43c52..77f54bfd0e334 100755 --- a/tests/runner.py +++ b/tests/runner.py @@ -14174,31 +14174,20 @@ def test_enet(self): # TODO use this once we have listen server support # def test_enet(self): - # # build a native version of the enet lib - # try_delete(self.in_dir('enet_native')) - # shutil.copytree(path_from_root('tests', 'enet'), self.in_dir('enet_native')) + # try_delete(self.in_dir('enet')) + # shutil.copytree(path_from_root('tests', 'enet'), self.in_dir('enet')) # pwd = os.getcwd() - # os.chdir(self.in_dir('enet_native')) - # Popen(['./configure']).communicate() - # Popen(['make']).communicate() - # enet_native = [self.in_dir('enet_native', '.libs', 'libenet.a'), '-I'+path_from_root('tests', 'enet', 'include')] - # os.chdir(pwd) - - # # build a transpiled version of enet lib - # try_delete(self.in_dir('enet_emscripten')) - # shutil.copytree(path_from_root('tests', 'enet'), self.in_dir('enet_emscripten')) - # pwd = os.getcwd() - # os.chdir(self.in_dir('enet_emscripten')) + # os.chdir(self.in_dir('enet')) # Popen([PYTHON, path_from_root('emconfigure'), './configure']).communicate() # Popen([PYTHON, path_from_root('emmake'), 'make']).communicate() - # enet_emscripten = [self.in_dir('enet_emscripten', '.libs', 'libenet.a'), '-I'+path_from_root('tests', 'enet', 'include')] + # enet = [self.in_dir('enet', '.libs', 'libenet.a'), '-I'+path_from_root('tests', 'enet', 'include')] # os.chdir(pwd) # for harness in [ - # self.CompiledServerHarness(os.path.join('sockets', 'test_enet_server.c'), ['-DSOCKK=9010'] + enet_native, 9011, 9010) + # self.CompiledServerHarness(os.path.join('sockets', 'test_enet_server.c'), ['-DSOCKK=9010'] + enet, 9011, 9010) # ]: # with harness: - # self.btest(os.path.join('sockets', 'test_enet_client.c'), expected='0', args=['-DSOCKK=9011'] + enet_emscripten) + # self.btest(os.path.join('sockets', 'test_enet_client.c'), expected='0', args=['-DSOCKK=9011'] + enet) elif 'benchmark' in str(sys.argv): # Benchmarks. Run them with argument |benchmark|. To run a specific test, do From 5bb74d2c5ed57a7b52e1a3d01404edfb7a9945ea Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Sat, 10 Aug 2013 21:01:10 -0700 Subject: [PATCH 036/112] make test runner more lenient in parsing asm.js compilation message --- tests/runner.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/runner.py b/tests/runner.py index f7a0fa26e0ba0..9a8670b623898 100755 --- a/tests/runner.py +++ b/tests/runner.py @@ -275,7 +275,7 @@ def validate_asmjs(self, err): print >> sys.stderr, "[was asm.js'ified]" elif 'asm.js' in err: # if no asm.js error, then not an odin build raise Exception("did NOT asm.js'ify") - err = '\n'.join(filter(lambda line: 'successfully compiled asm.js code' not in line, err.split('\n'))) + err = '\n'.join(filter(lambda line: 'uccessfully compiled asm.js code' not in line, err.split('\n'))) return err def run_generated_code(self, engine, filename, args=[], check_timeout=True, output_nicerizer=None): From 31c977f6797323673208b7c4186a0e69dee4eca6 Mon Sep 17 00:00:00 2001 From: ToadKing Date: Sun, 11 Aug 2013 16:14:27 -0400 Subject: [PATCH 037/112] fix bug in alcDestroyContext --- src/library_openal.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/library_openal.js b/src/library_openal.js index c55415b8cb610..266772b2d4482 100644 --- a/src/library_openal.js +++ b/src/library_openal.js @@ -41,7 +41,7 @@ var LibraryOpenAL = { alcDestroyContext: function(context) { // Stop playback, etc - clearInterval(context.interval); + clearInterval(AL.contexts[context - 1].interval); }, alcCloseDevice: function(device) { From 38adc363fae517382e5b59308389d1ceb75a7461 Mon Sep 17 00:00:00 2001 From: ToadKing Date: Sun, 11 Aug 2013 17:55:38 -0400 Subject: [PATCH 038/112] use performance timers for usleep if available --- src/library.js | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/library.js b/src/library.js index fedb87609ff9b..815badc1b560c 100644 --- a/src/library.js +++ b/src/library.js @@ -1413,9 +1413,16 @@ LibraryManager.library = { // http://pubs.opengroup.org/onlinepubs/000095399/functions/usleep.html // We're single-threaded, so use a busy loop. Super-ugly. var msec = useconds / 1000; - var start = Date.now(); - while (Date.now() - start < msec) { - // Do nothing. + if (ENVIRONMENT_IS_WEB && window['performance'] && window['performance']['now']) { + var start = window['performance']['now'](); + while (window['performance']['now']() - start < msec) { + // Do nothing. + } + } else { + var start = Date.now(); + while (Date.now() - start < msec) { + // Do nothing. + } } return 0; }, From 8cb83003709aa90a3410b6352552342ec97d5116 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Sun, 11 Aug 2013 21:57:15 -0700 Subject: [PATCH 039/112] experimental toFloat32 option --- emscripten.py | 2 ++ src/parseTools.js | 17 ++++++++++++----- src/preamble.js | 7 +++++++ src/settings.js | 1 + 4 files changed, 22 insertions(+), 5 deletions(-) diff --git a/emscripten.py b/emscripten.py index 217fd6d5a5c82..1b1284c71706e 100755 --- a/emscripten.py +++ b/emscripten.py @@ -423,6 +423,8 @@ def fix_item(item): math_envs = ['Math.min'] # TODO: move min to maths asm_setup += '\n'.join(['var %s = %s;' % (f.replace('.', '_'), f) for f in math_envs]) + if settings['TO_FLOAT32']: maths += ['Math.toFloat32'] + basic_funcs = ['abort', 'assert', 'asmPrintInt', 'asmPrintFloat'] + [m.replace('.', '_') for m in math_envs] if settings['RESERVED_FUNCTION_POINTERS'] > 0: basic_funcs.append('jsCall') if settings['SAFE_HEAP']: basic_funcs += ['SAFE_HEAP_LOAD', 'SAFE_HEAP_STORE', 'SAFE_HEAP_CLEAR'] diff --git a/src/parseTools.js b/src/parseTools.js index b655d13e9c074..f11c867a53bbc 100644 --- a/src/parseTools.js +++ b/src/parseTools.js @@ -2020,6 +2020,13 @@ function makeIsNaN(value) { return 'isNaN(' + value + ')'; } +function makeFloat(value, type) { + if (TO_FLOAT32 && type == 'float') { + return 'Math.toFloat32(' + value + ')'; + } + return value; +} + // fptoui and fptosi are not in these, because we need to be careful about what we do there. We can't // just sign/unsign the input first. var UNSIGNED_OP = set('udiv', 'urem', 'uitofp', 'zext', 'lshr'); @@ -2275,11 +2282,11 @@ function processMathop(item) { return idents[0] + ' >>> ' + idents[1]; } // basic float ops - case 'fadd': return getFastValue(idents[0], '+', idents[1], item.type); - case 'fsub': return getFastValue(idents[0], '-', idents[1], item.type); - case 'fdiv': return getFastValue(idents[0], '/', idents[1], item.type); - case 'fmul': return getFastValue(idents[0], '*', idents[1], item.type); - case 'frem': return getFastValue(idents[0], '%', idents[1], item.type); + case 'fadd': return makeFloat(getFastValue(idents[0], '+', idents[1], item.type), item.type); + case 'fsub': return makeFloat(getFastValue(idents[0], '-', idents[1], item.type), item.type); + case 'fdiv': return makeFloat(getFastValue(idents[0], '/', idents[1], item.type), item.type); + case 'fmul': return makeFloat(getFastValue(idents[0], '*', idents[1], item.type), item.type); + case 'frem': return makeFloat(getFastValue(idents[0], '%', idents[1], item.type), item.type); case 'uitofp': case 'sitofp': return asmCoercion(idents[0], 'double', op[0]); case 'fptoui': case 'fptosi': return makeRounding(idents[0], bitsLeft, op === 'fptosi', true); diff --git a/src/preamble.js b/src/preamble.js index 95bf2dc252062..0f3f52c9b8e7b 100644 --- a/src/preamble.js +++ b/src/preamble.js @@ -853,6 +853,13 @@ Math['imul'] = function(a, b) { #endif Math.imul = Math['imul']; +#if TO_FLOAT32 +if (!Math['toFloat32']) Math['toFloat32'] = function(x) { + return x; +}; +Math.toFloat32 = Math['toFloat32']; +#endif + // A counter of dependencies for calling run(). If we need to // do asynchronous work before running, increment this and // decrement it. Incrementing must happen in a place like diff --git a/src/settings.js b/src/settings.js index 3515091d6e165..3ecac040ccdb6 100644 --- a/src/settings.js +++ b/src/settings.js @@ -108,6 +108,7 @@ var PRECISE_I64_MATH = 1; // If enabled, i64 addition etc. is emulated - which i var PRECISE_I32_MUL = 1; // If enabled, i32 multiplication is done with full precision, which means it is // correct even if the value exceeds the JS double-integer limit of ~52 bits (otherwise, // rounding will occur above that range). +var TO_FLOAT32 = 0; // Use Math.toFloat32 var CLOSURE_ANNOTATIONS = 0; // If set, the generated code will be annotated for the closure // compiler. This potentially lets closure optimize the code better. From 8d3994c9eb604408fb0a325712c63b96827dee90 Mon Sep 17 00:00:00 2001 From: Anthony Pesch Date: Sun, 11 Aug 2013 21:48:58 -0700 Subject: [PATCH 040/112] initial work on splitting up runner.py --- tests/runner.py | 15237 +------------------------------------- tests/test_benchmark.py | 507 ++ tests/test_browser.py | 1365 ++++ tests/test_core.py | 10074 +++++++++++++++++++++++++ tests/test_other.py | 1913 +++++ tests/test_sanity.py | 522 ++ tests/test_sockets.py | 200 + tools/shared.py | 139 +- 8 files changed, 15003 insertions(+), 14954 deletions(-) create mode 100644 tests/test_benchmark.py create mode 100644 tests/test_browser.py create mode 100644 tests/test_core.py create mode 100644 tests/test_other.py create mode 100644 tests/test_sanity.py create mode 100644 tests/test_sockets.py diff --git a/tests/runner.py b/tests/runner.py index 9a8670b623898..b9b9244531516 100755 --- a/tests/runner.py +++ b/tests/runner.py @@ -18,47 +18,6 @@ from subprocess import Popen, PIPE, STDOUT import os, unittest, tempfile, shutil, time, inspect, sys, math, glob, re, difflib, webbrowser, hashlib, threading, platform, BaseHTTPServer, multiprocessing, functools, stat, string -if len(sys.argv) == 1: - print ''' -============================================================================== -Running the main part of the test suite. Don't forget to run the other parts! - - sanity - tests for first run, etc., modifies ~/.emscripten - benchmark - run before and after each set of changes before pushing to - master, verify no regressions - browser - runs pages in a web browser - -There are also commands to run specific subsets of the test suite: - - browser sockets - runs websocket networking tests - browser audio - runs audio tests in a web browser (requires human verification) - -To run one of those parts, do something like - - python tests/runner.py sanity - -To run a specific set of tests, you can do things like - - python tests/runner.py o1 - -(that runs the o1 (-O1) tests). You can run individual tests with - - python tests/runner.py test_hello_world - -Combinations work too, for example - - python tests/runner.py browser.test_sdl_image - -In the main test suite, you can run all variations (O0, O1, O2, etc.) of -an individual test with - - python tests/runner.py ALL.test_hello_world - -============================================================================== - -''' - time.sleep(2) - # Setup __rootpath__ = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) @@ -76,10 +35,13 @@ def path_from_root(*pathelems): raise Exception('Cannot find "COMPILER_OPTS" definition. Is %s set up properly? You may need to copy the template settings file into it.' % EM_CONFIG) # Core test runner class, shared between normal tests and benchmarks - checked_sanity = False +test_modes = ['default', 'o1', 'o2', 'asm1', 'asm2', 'asm2g', 'asm2x86', 's_0_0', 's_0_1'] +test_index = 0 +js_engine_override = None class RunnerCore(unittest.TestCase): + emcc_args = None save_dir = os.environ.get('EM_SAVE_DIR') save_JS = 0 stderr_redirect = STDOUT # This avoids cluttering the test runner output, which is stderr too, with compiler warnings etc. @@ -91,9 +53,7 @@ def skipme(self): # used by tests we ask on the commandline to be skipped, see r return self.skip('requested to be skipped') def setUp(self): - global Settings Settings.reset() - Settings = tools.shared.Settings self.banned_js_engines = [] if not self.save_dir: dirname = tempfile.mkdtemp(prefix='emscripten_test_' + self.__class__.__name__ + '_', dir=TEMP_DIR) @@ -441,14895 +401,376 @@ def setup_runtimelink_test(self): return 0; } ''' - return (main, supp) -################################################################################################### - -sys.argv = map(lambda arg: arg if not arg.startswith('test_') else 'default.' + arg, sys.argv) - -test_modes = ['default', 'o1', 'o2', 'asm1', 'asm2', 'asm2g', 'asm2x86', 's_0_0', 's_0_1'] - -test_index = 0 - -if 'benchmark' not in str(sys.argv) and 'sanity' not in str(sys.argv) and 'browser' not in str(sys.argv): - # Tests - - print "Running Emscripten tests..." - - if len(sys.argv) == 2 and sys.argv[1].startswith('ALL.'): - ignore, test = sys.argv[1].split('.') - print 'Running all test modes on test "%s"' % test - sys.argv = [sys.argv[0]] + map(lambda mode: mode+'.'+test, test_modes) - - class T(RunnerCore): # Short name, to make it more fun to use manually on the commandline - ## Does a complete test - builds, runs, checks output, etc. - def do_run(self, src, expected_output, args=[], output_nicerizer=None, output_processor=None, no_build=False, main_file=None, additional_files=[], js_engines=None, post_build=None, basename='src.cpp', libraries=[], includes=[], force_c=False, build_ll_hook=None, extra_emscripten_args=[]): - if force_c or (main_file is not None and main_file[-2:]) == '.c': - basename = 'src.c' - Building.COMPILER = to_cc(Building.COMPILER) - - dirname = self.get_dir() - filename = os.path.join(dirname, basename) - if not no_build: - self.build(src, dirname, filename, main_file=main_file, additional_files=additional_files, libraries=libraries, includes=includes, - build_ll_hook=build_ll_hook, extra_emscripten_args=extra_emscripten_args, post_build=post_build) - - # Run in both JavaScript engines, if optimizing - significant differences there (typed arrays) - if js_engines is None: - js_engines = JS_ENGINES - if Settings.USE_TYPED_ARRAYS: - js_engines = filter(lambda engine: engine != V8_ENGINE, js_engines) # V8 issue 1822 - js_engines = filter(lambda engine: engine not in self.banned_js_engines, js_engines) - if len(js_engines) == 0: return self.skip('No JS engine present to run this test with. Check %s and the paths therein.' % EM_CONFIG) - for engine in js_engines: - js_output = self.run_generated_code(engine, filename + '.o.js', args, output_nicerizer=output_nicerizer) - self.assertContained(expected_output, js_output.replace('\r\n', '\n')) - self.assertNotContained('ERROR', js_output) - - #shutil.rmtree(dirname) # TODO: leave no trace in memory. But for now nice for debugging - - if self.save_JS: - global test_index - self.hardcode_arguments(filename + '.o.js', args) - shutil.copyfile(filename + '.o.js', os.path.join(TEMP_DIR, str(test_index) + '.js')) - test_index += 1 - - # No building - just process an existing .ll file (or .bc, which we turn into .ll) - def do_ll_run(self, ll_file, expected_output=None, args=[], js_engines=None, output_nicerizer=None, post_build=None, force_recompile=False, build_ll_hook=None, extra_emscripten_args=[]): - filename = os.path.join(self.get_dir(), 'src.cpp') - - self.prep_ll_run(filename, ll_file, force_recompile, build_ll_hook) - - self.ll_to_js(filename, extra_emscripten_args, post_build) - - self.do_run(None, - expected_output, - args, - no_build=True, - js_engines=js_engines, - output_nicerizer=output_nicerizer, - post_build=None) # post_build was already done in ll_to_js, this do_run call is just to test the output - - def is_le32(self): - return not ('i386-pc-linux-gnu' in COMPILER_OPTS or self.env.get('EMCC_LLVM_TARGET') == 'i386-pc-linux-gnu') - - def test_hello_world(self): - src = ''' - #include - int main() - { - printf("hello, world!\\n"); - return 0; - } - ''' - self.do_run(src, 'hello, world!') + ## Does a complete test - builds, runs, checks output, etc. + def do_run(self, src, expected_output, args=[], output_nicerizer=None, output_processor=None, no_build=False, main_file=None, additional_files=[], js_engines=None, post_build=None, basename='src.cpp', libraries=[], includes=[], force_c=False, build_ll_hook=None, extra_emscripten_args=[]): + if force_c or (main_file is not None and main_file[-2:]) == '.c': + basename = 'src.c' + Building.COMPILER = to_cc(Building.COMPILER) + + dirname = self.get_dir() + filename = os.path.join(dirname, basename) + if not no_build: + self.build(src, dirname, filename, main_file=main_file, additional_files=additional_files, libraries=libraries, includes=includes, + build_ll_hook=build_ll_hook, extra_emscripten_args=extra_emscripten_args, post_build=post_build) + + # Run in both JavaScript engines, if optimizing - significant differences there (typed arrays) + if js_engines is None: + js_engines = JS_ENGINES + if Settings.USE_TYPED_ARRAYS: + js_engines = filter(lambda engine: engine != V8_ENGINE, js_engines) # V8 issue 1822 + js_engines = filter(lambda engine: engine not in self.banned_js_engines, js_engines) + if len(js_engines) == 0: return self.skip('No JS engine present to run this test with. Check %s and the paths therein.' % EM_CONFIG) + for engine in js_engines: + js_output = self.run_generated_code(engine, filename + '.o.js', args, output_nicerizer=output_nicerizer) + self.assertContained(expected_output, js_output.replace('\r\n', '\n')) + self.assertNotContained('ERROR', js_output) + + #shutil.rmtree(dirname) # TODO: leave no trace in memory. But for now nice for debugging + + if self.save_JS: + global test_index + self.hardcode_arguments(filename + '.o.js', args) + shutil.copyfile(filename + '.o.js', os.path.join(TEMP_DIR, str(test_index) + '.js')) + test_index += 1 + + # No building - just process an existing .ll file (or .bc, which we turn into .ll) + def do_ll_run(self, ll_file, expected_output=None, args=[], js_engines=None, output_nicerizer=None, post_build=None, force_recompile=False, build_ll_hook=None, extra_emscripten_args=[]): + filename = os.path.join(self.get_dir(), 'src.cpp') + + self.prep_ll_run(filename, ll_file, force_recompile, build_ll_hook) - assert 'EMSCRIPTEN_GENERATED_FUNCTIONS' not in open(self.in_dir('src.cpp.o.js')).read(), 'must not emit this unneeded internal thing' - - def test_intvars(self): - if self.emcc_args == None: return self.skip('needs ta2') - - src = ''' - #include - int global = 20; - int *far; - int main() - { - int x = 5; - int y = x+17; - int z = (y-1)/2; // Should stay an integer after division! - y += 1; - int w = x*3+4; - int k = w < 15 ? 99 : 101; - far = &k; - *far += global; - int i = k > 100; // Should be an int, not a bool! - int j = i << 6; - j >>= 1; - j = j ^ 5; - int h = 1; - h |= 0; - int p = h; - p &= 0; - printf("*%d,%d,%d,%d,%d,%d,%d,%d,%d*\\n", x, y, z, w, k, i, j, h, p); - - long hash = -1; - size_t perturb; - int ii = 0; - for (perturb = hash; ; perturb >>= 5) { - printf("%d:%d", ii, perturb); - ii++; - if (ii == 9) break; - printf(","); - } - printf("*\\n"); - printf("*%.1d,%.2d*\\n", 56, 9); - - // Fixed-point math on 64-bit ints. Tricky to support since we have no 64-bit shifts in JS - { - struct Fixed { - static int Mult(int a, int b) { - return ((long long)a * (long long)b) >> 16; - } - }; - printf("fixed:%d\\n", Fixed::Mult(150000, 140000)); - } - - printf("*%ld*%p\\n", (long)21, &hash); // The %p should not enter an infinite loop! - return 0; - } - ''' - self.do_run(src, '*5,23,10,19,121,1,37,1,0*\n0:-1,1:134217727,2:4194303,3:131071,4:4095,5:127,6:3,7:0,8:0*\n*56,09*\nfixed:320434\n*21*') - - def test_sintvars(self): - Settings.CORRECT_SIGNS = 1 # Relevant to this test - src = ''' - #include - struct S { - char *match_start; - char *strstart; - }; - int main() - { - struct S _s; - struct S *s = &_s; - unsigned short int sh; - - s->match_start = (char*)32522; - s->strstart = (char*)(32780); - printf("*%d,%d,%d*\\n", (int)s->strstart, (int)s->match_start, (int)(s->strstart - s->match_start)); - sh = s->strstart - s->match_start; - printf("*%d,%d*\\n", sh, sh>>7); - - s->match_start = (char*)32999; - s->strstart = (char*)(32780); - printf("*%d,%d,%d*\\n", (int)s->strstart, (int)s->match_start, (int)(s->strstart - s->match_start)); - sh = s->strstart - s->match_start; - printf("*%d,%d*\\n", sh, sh>>7); - } - ''' - output = '*32780,32522,258*\n*258,2*\n*32780,32999,-219*\n*65317,510*' - Settings.CORRECT_OVERFLOWS = 0 # We should not need overflow correction to get this right - self.do_run(src, output, force_c=True) - - def test_i64(self): - if Settings.USE_TYPED_ARRAYS != 2: return self.skip('i64 mode 1 requires ta2') - - src = ''' - #include - int main() - { - long long a = 0x2b00505c10; - long long b = a >> 29; - long long c = a >> 32; - long long d = a >> 34; - printf("*%Ld,%Ld,%Ld,%Ld*\\n", a, b, c, d); - unsigned long long ua = 0x2b00505c10; - unsigned long long ub = ua >> 29; - unsigned long long uc = ua >> 32; - unsigned long long ud = ua >> 34; - printf("*%Ld,%Ld,%Ld,%Ld*\\n", ua, ub, uc, ud); - - long long x = 0x0000def123450789ULL; // any bigger than this, and we - long long y = 0x00020ef123456089ULL; // start to run into the double precision limit! - printf("*%Ld,%Ld,%Ld,%Ld,%Ld*\\n", x, y, x | y, x & y, x ^ y, x >> 2, y << 2); - - printf("*"); - long long z = 13; - int n = 0; - while (z > 1) { - printf("%.2f,", (float)z); // these must be integers! - z = z >> 1; - n++; - } - printf("*%d*\\n", n); - return 0; - } - ''' - self.do_run(src, '*184688860176,344,43,10*\n*184688860176,344,43,10*\n*245127260211081,579378795077769,808077213656969,16428841631881,791648372025088*\n*13.00,6.00,3.00,*3*') + self.ll_to_js(filename, extra_emscripten_args, post_build) - src = r''' - #include - #include - #include + self.do_run(None, + expected_output, + args, + no_build=True, + js_engines=js_engines, + output_nicerizer=output_nicerizer, + post_build=None) # post_build was already done in ll_to_js, this do_run call is just to test the output + + +# Run a server and a web page. When a test runs, we tell the server about it, +# which tells the web page, which then opens a window with the test. Doing +# it this way then allows the page to close() itself when done. +def harness_server_func(q): + class TestServerHandler(BaseHTTPServer.BaseHTTPRequestHandler): + def do_GET(s): + s.send_response(200) + s.send_header("Content-type", "text/html") + s.end_headers() + if s.path == '/run_harness': + s.wfile.write(open(path_from_root('tests', 'browser_harness.html')).read()) + else: + result = 'False' + if not q.empty(): + result = q.get() + s.wfile.write(result) + s.wfile.close() + def log_request(code=0, size=0): + # don't log; too noisy + pass + httpd = BaseHTTPServer.HTTPServer(('localhost', 9999), TestServerHandler) + httpd.serve_forever() # test runner will kill us + +def server_func(dir, q): + class TestServerHandler(BaseHTTPServer.BaseHTTPRequestHandler): + def do_GET(s): + if 'report_' in s.path: + q.put(s.path) + else: + filename = s.path[1:] + if os.path.exists(filename): + s.send_response(200) + s.send_header("Content-type", "text/html") + s.end_headers() + s.wfile.write(open(filename).read()) + s.wfile.close() + else: + s.send_response(500) + s.send_header("Content-type", "text/html") + s.end_headers() + def log_request(code=0, size=0): + # don't log; too noisy + pass + os.chdir(dir) + httpd = BaseHTTPServer.HTTPServer(('localhost', 8888), TestServerHandler) + httpd.serve_forever() # test runner will kill us + +class BrowserCore(RunnerCore): + def __init__(self, *args, **kwargs): + super(BrowserCore, self).__init__(*args, **kwargs) + + @classmethod + def setUpClass(self): + super(BrowserCore, self).setUpClass() + self.harness_queue = multiprocessing.Queue() + self.harness_server = multiprocessing.Process(target=harness_server_func, args=(self.harness_queue,)) + self.harness_server.start() + print '[Browser harness server on process %d]' % self.harness_server.pid + webbrowser.open_new('http://localhost:9999/run_harness') + + @classmethod + def tearDownClass(self): + super(BrowserCore, self).tearDownClass() + self.harness_server.terminate() + print '[Browser harness server terminated]' + # On Windows, shutil.rmtree() in tearDown() raises this exception if we do not wait a bit: + # WindowsError: [Error 32] The process cannot access the file because it is being used by another process. + time.sleep(0.1) + + def run_browser(self, html_file, message, expectedResult=None): + if expectedResult is not None: + try: + queue = multiprocessing.Queue() + server = multiprocessing.Process(target=functools.partial(server_func, self.get_dir()), args=(queue,)) + server.start() + self.harness_queue.put('http://localhost:8888/' + html_file) + output = '[no http server activity]' + start = time.time() + while time.time() - start < 60: + if not queue.empty(): + output = queue.get() + break + time.sleep(0.1) - int64_t returner1() { return 0x0000def123450789ULL; } - int64_t returner2(int test) { - while (test > 10) test /= 2; // confuse the compiler so it doesn't eliminate this function - return test > 5 ? 0x0000def123450123ULL : 0ULL; - } + self.assertIdentical(expectedResult, output) + finally: + server.terminate() + time.sleep(0.1) # see comment about Windows above + else: + webbrowser.open_new(os.path.abspath(html_file)) + print 'A web browser window should have opened a page containing the results of a part of this test.' + print 'You need to manually look at the page to see that it works ok: ' + message + print '(sleeping for a bit to keep the directory alive for the web browser..)' + time.sleep(5) + print '(moving on..)' + + def with_report_result(self, code): + return r''' + #if EMSCRIPTEN + #include + #define REPORT_RESULT_INTERNAL(sync) \ + char output[1000]; \ + sprintf(output, \ + "xhr = new XMLHttpRequest();" \ + "xhr.open('GET', 'http://localhost:8888/report_result?%d'%s);" \ + "xhr.send();", result, sync ? ", false" : ""); \ + emscripten_run_script(output); \ + emscripten_run_script("setTimeout(function() { window.close() }, 1000)"); // comment this out to keep the test runner window open to debug + #define REPORT_RESULT() REPORT_RESULT_INTERNAL(0) + #endif +''' + code - void modifier1(int64_t t) { - t |= 12; - printf("m1: %Ld\n", t); - } - void modifier2(int64_t &t) { - t |= 12; - } + def reftest(self, expected): + # make sure the pngs used here have no color correction, using e.g. + # pngcrush -rem gAMA -rem cHRM -rem iCCP -rem sRGB infile outfile + basename = os.path.basename(expected) + shutil.copyfile(expected, os.path.join(self.get_dir(), basename)) + open(os.path.join(self.get_dir(), 'reftest.js'), 'w').write(''' + var Module = eval('Module'); + function doReftest() { + if (doReftest.done) return; + doReftest.done = true; + var img = new Image(); + img.onload = function() { + assert(img.width == Module.canvas.width, 'Invalid width: ' + Module.canvas.width + ', should be ' + img.width); + assert(img.height == Module.canvas.height, 'Invalid height: ' + Module.canvas.height + ', should be ' + img.height); + + var canvas = document.createElement('canvas'); + canvas.width = img.width; + canvas.height = img.height; + var ctx = canvas.getContext('2d'); + ctx.drawImage(img, 0, 0); + var expected = ctx.getImageData(0, 0, img.width, img.height).data; + + var actualUrl = Module.canvas.toDataURL(); + var actualImage = new Image(); + actualImage.onload = function() { + /* + document.body.appendChild(img); // for comparisons + var div = document.createElement('div'); + div.innerHTML = '^=expected, v=actual'; + document.body.appendChild(div); + document.body.appendChild(actualImage); // to grab it for creating the test reference + */ - int truthy() { - int x = time(0); - while (x > 10) { - x |= 7; - x /= 2; + var actualCanvas = document.createElement('canvas'); + actualCanvas.width = actualImage.width; + actualCanvas.height = actualImage.height; + var actualCtx = actualCanvas.getContext('2d'); + actualCtx.drawImage(actualImage, 0, 0); + var actual = actualCtx.getImageData(0, 0, actualImage.width, actualImage.height).data; + + var total = 0; + var width = img.width; + var height = img.height; + for (var x = 0; x < width; x++) { + for (var y = 0; y < height; y++) { + total += Math.abs(expected[y*width*4 + x*4 + 0] - actual[y*width*4 + x*4 + 0]); + total += Math.abs(expected[y*width*4 + x*4 + 1] - actual[y*width*4 + x*4 + 1]); + total += Math.abs(expected[y*width*4 + x*4 + 2] - actual[y*width*4 + x*4 + 2]); + } } - return x < 3; - } + var wrong = Math.floor(total / (img.width*img.height*3)); // floor, to allow some margin of error for antialiasing - struct IUB { - int c; - long long d; + xhr = new XMLHttpRequest(); + xhr.open('GET', 'http://localhost:8888/report_result?' + wrong); + xhr.send(); + setTimeout(function() { window.close() }, 1000); }; - - IUB iub[] = { - { 55, 17179869201 }, - { 122, 25769803837 }, - }; - - int main(int argc, char **argv) - { - int64_t x1 = 0x1234def123450789ULL; - int64_t x2 = 0x1234def123450788ULL; - int64_t x3 = 0x1234def123450789ULL; - printf("*%Ld\n%d,%d,%d,%d,%d\n%d,%d,%d,%d,%d*\n", x1, x1==x2, x1x2, x1>=x2, // note: some rounding in the printing! - x1==x3, x1x3, x1>=x3); - printf("*%Ld*\n", returner1()); - printf("*%Ld*\n", returner2(30)); - - uint64_t maxx = -1ULL; - printf("*%Lu*\n*%Lu*\n", maxx, maxx >> 5); - - // Make sure params are not modified if they shouldn't be - int64_t t = 123; - modifier1(t); - printf("*%Ld*\n", t); - modifier2(t); - printf("*%Ld*\n", t); - - // global structs with i64s - printf("*%d,%Ld*\n*%d,%Ld*\n", iub[0].c, iub[0].d, iub[1].c, iub[1].d); - - // Bitshifts - { - int64_t a = -1; - int64_t b = a >> 29; - int64_t c = a >> 32; - int64_t d = a >> 34; - printf("*%Ld,%Ld,%Ld,%Ld*\n", a, b, c, d); - uint64_t ua = -1; - int64_t ub = ua >> 29; - int64_t uc = ua >> 32; - int64_t ud = ua >> 34; - printf("*%Ld,%Ld,%Ld,%Ld*\n", ua, ub, uc, ud); - } - - // Nonconstant bitshifts - { - int64_t a = -1; - int64_t b = a >> (29 - argc + 1); - int64_t c = a >> (32 - argc + 1); - int64_t d = a >> (34 - argc + 1); - printf("*%Ld,%Ld,%Ld,%Ld*\n", a, b, c, d); - uint64_t ua = -1; - int64_t ub = ua >> (29 - argc + 1); - int64_t uc = ua >> (32 - argc + 1); - int64_t ud = ua >> (34 - argc + 1); - printf("*%Ld,%Ld,%Ld,%Ld*\n", ua, ub, uc, ud); - } - - // Math mixtures with doubles - { - uint64_t a = 5; - double b = 6.8; - uint64_t c = a * b; - if (truthy()) printf("*%d,%d,%d*\n", (int)&a, (int)&b, (int)&c); // printing addresses prevents optimizations - printf("*prod:%llu*\n", c); - } - - // Basic (rounded, for now) math. Just check compilation. - int64_t a = 0x1234def123450789ULL; - a--; if (truthy()) a--; // confuse optimizer - int64_t b = 0x1234000000450789ULL; - b++; if (truthy()) b--; // confuse optimizer - printf("*%Ld,%Ld,%Ld,%Ld*\n", (a+b)/5000, (a-b)/5000, (a*3)/5000, (a/5)/5000); - - a -= 17; if (truthy()) a += 5; // confuse optimizer - b -= 17; if (truthy()) b += 121; // confuse optimizer - printf("*%Lx,%Lx,%Lx,%Lx*\n", b - a, b - a/2, b/2 - a, b - 20); - - if (truthy()) a += 5/b; // confuse optimizer - if (truthy()) b += 121*(3+a/b); // confuse optimizer - printf("*%Lx,%Lx,%Lx,%Lx*\n", a - b, a - b/2, a/2 - b, a - 20); - - return 0; - } - ''' - self.do_run(src, '*1311918518731868041\n' + - '0,0,0,1,1\n' + - '1,0,1,0,1*\n' + - '*245127260211081*\n' + - '*245127260209443*\n' + - '*18446744073709551615*\n' + - '*576460752303423487*\n' + - 'm1: 127\n' + - '*123*\n' + - '*127*\n' + - '*55,17179869201*\n' + - '*122,25769803837*\n' + - '*-1,-1,-1,-1*\n' + - '*-1,34359738367,4294967295,1073741823*\n' + - '*-1,-1,-1,-1*\n' + - '*-1,34359738367,4294967295,1073741823*\n' + - '*prod:34*\n' + - '*524718382041609,49025451137,787151111239120,52476740749274*\n' + - '*ffff210edd000002,91990876ea283be,f6e5210edcdd7c45,1234000000450765*\n' + - '*def122fffffe,91adef1232283bb,f6e66f78915d7c42,1234def123450763*\n') - - src = r''' - #include - #include - - int main() - { - long long i,j,k; - - i = 0; - j = -1, - k = 1; - - printf( "*\n" ); - printf( "%s\n", i > j ? "Ok": "Fail" ); - printf( "%s\n", k > i ? "Ok": "Fail" ); - printf( "%s\n", k > j ? "Ok": "Fail" ); - printf( "%s\n", i < j ? "Fail": "Ok" ); - printf( "%s\n", k < i ? "Fail": "Ok" ); - printf( "%s\n", k < j ? "Fail": "Ok" ); - printf( "%s\n", (i-j) >= k ? "Ok": "Fail" ); - printf( "%s\n", (i-j) <= k ? "Ok": "Fail" ); - printf( "%s\n", i > std::numeric_limits::min() ? "Ok": "Fail" ); - printf( "%s\n", i < std::numeric_limits::max() ? "Ok": "Fail" ); - printf( "*\n" ); - } - ''' - - self.do_run(src, '*\nOk\nOk\nOk\nOk\nOk\nOk\nOk\nOk\nOk\nOk\n*') - - # stuff that also needs sign corrections - - Settings.CORRECT_SIGNS = 1 - - src = r''' - #include - #include - - int main() - { - // i32 vs i64 - int32_t small = -1; - int64_t large = -1; - printf("*%d*\n", small == large); - small++; - printf("*%d*\n", small == large); - uint32_t usmall = -1; - uint64_t ularge = -1; - printf("*%d*\n", usmall == ularge); - return 0; - } - ''' - - self.do_run(src, '*1*\n*0*\n*0*\n') - - def test_i64_b(self): - if Settings.USE_TYPED_ARRAYS != 2: return self.skip('full i64 stuff only in ta2') - - src = r''' - #include - #include - - typedef long long int64; - - #define PRMJ_USEC_PER_SEC 1000000L - - int main(int argc, char * argv[]) { - int64 sec = 1329409675 + argc; - int64 usec = 2329509675; - int64 mul = int64(sec) * PRMJ_USEC_PER_SEC; - int64 add = mul + int64(usec); - int add_low = add; - int add_high = add >> 32; - printf("*%lld,%lld,%u,%u*\n", mul, add, add_low, add_high); - int64 x = sec + (usec << 25); - x >>= argc*3; - printf("*%llu*\n", x); - return 0; - } - ''' - - self.do_run(src, '*1329409676000000,1329412005509675,3663280683,309527*\n*9770671914067409*\n') - - def test_i64_cmp(self): - if Settings.USE_TYPED_ARRAYS != 2: return self.skip('full i64 stuff only in ta2') - - src = r''' - #include - - typedef long long int64; - - bool compare(int64 val) { - return val == -12; - } - - bool compare2(int64 val) { - return val < -12; - } - - int main(int argc, char * argv[]) { - printf("*%d,%d,%d,%d,%d,%d*\n", argc, compare(argc-1-12), compare(1000+argc), compare2(argc-1-10), compare2(argc-1-14), compare2(argc+1000)); - return 0; - } - ''' - - self.do_run(src, '*1,1,0,0,1,0*\n') - - def test_i64_cmp2(self): - if Settings.USE_TYPED_ARRAYS != 2: return self.skip('full i64 stuff only in ta2') - - src = r''' - #include - #include - - typedef int32_t INT32; - typedef int64_t INT64; - typedef uint8_t UINT8; - - void interface_clock_changed() - { - UINT8 m_divshift; - INT32 m_divisor; - - //INT64 attos = m_attoseconds_per_cycle; - INT64 attos = 279365114840; - m_divshift = 0; - while (attos >= (1UL << 31)) - { - m_divshift++; - printf("m_divshift is %i, on %Ld >?= %lu\n", m_divshift, attos, 1UL << 31); - attos >>= 1; - } - m_divisor = attos; - - printf("m_divisor is %i\n",m_divisor); - } - - int main() { - interface_clock_changed(); - return 0; - } - ''' - self.do_run(src, '''m_divshift is 1, on 279365114840 >?= 2147483648 -m_divshift is 2, on 139682557420 >?= 2147483648 -m_divshift is 3, on 69841278710 >?= 2147483648 -m_divshift is 4, on 34920639355 >?= 2147483648 -m_divshift is 5, on 17460319677 >?= 2147483648 -m_divshift is 6, on 8730159838 >?= 2147483648 -m_divshift is 7, on 4365079919 >?= 2147483648 -m_divshift is 8, on 2182539959 >?= 2147483648 -m_divisor is 1091269979 -''') - - def test_i64_double(self): - if Settings.USE_TYPED_ARRAYS != 2: return self.skip('full i64 stuff only in ta2') - - - src = r''' - #include - - typedef long long int64; - #define JSDOUBLE_HI32_SIGNBIT 0x80000000 - - bool JSDOUBLE_IS_NEGZERO(double d) - { - union { - struct { - unsigned int lo, hi; - } s; - double d; - } x; - if (d != 0) - return false; - x.d = d; - return (x.s.hi & JSDOUBLE_HI32_SIGNBIT) != 0; - } - - bool JSINT64_IS_NEGZERO(int64 l) - { - union { - int64 i; - double d; - } x; - if (l != 0) - return false; - x.i = l; - return x.d == -0; - } - - int main(int argc, char * argv[]) { - printf("*%d,%d,%d,%d*\n", JSDOUBLE_IS_NEGZERO(0), JSDOUBLE_IS_NEGZERO(-0), JSDOUBLE_IS_NEGZERO(-1), JSDOUBLE_IS_NEGZERO(+1)); - printf("*%d,%d,%d,%d*\n", JSINT64_IS_NEGZERO(0), JSINT64_IS_NEGZERO(-0), JSINT64_IS_NEGZERO(-1), JSINT64_IS_NEGZERO(+1)); - return 0; - } - ''' - self.do_run(src, '*0,0,0,0*\n*1,1,0,0*\n') # same as gcc - - def test_i64_umul(self): - if Settings.USE_TYPED_ARRAYS != 2: return self.skip('full i64 stuff only in ta2') - - src = r''' - #include - #include - - typedef uint32_t UINT32; - typedef uint64_t UINT64; - - int main() { - volatile UINT32 testu32a = 2375724032U; - UINT32 bigu32 = 0xffffffffU; - volatile UINT64 testu64a = 14746250828952703000U; - - while ((UINT64)testu32a * (UINT64)bigu32 < testu64a) { - printf("testu64a is %llu\n", testu64a); - testu64a /= 2; - } - - return 0; - } - ''' - self.do_run(src, 'testu64a is 14746250828952703000\n') - - def test_i64_precise(self): - if Settings.USE_TYPED_ARRAYS != 2: return self.skip('full i64 stuff only in ta2') - - src = r''' - #include - #include - - int main() { - uint64_t x = 0, y = 0; - for (int i = 0; i < 64; i++) { - x += 1ULL << i; - y += x; - x /= 3; - y *= 5; - printf("unsigned %d: %llu,%llu,%llu,%llu,%llu,%llu,%llu,%llu,%llu\n", i, x, y, x+y, x-y, x*y, y ? x/y : 0, x ? y/x : 0, y ? x%y : 0, x ? y%x : 0); - } - int64_t x2 = 0, y2 = 0; - for (int i = 0; i < 64; i++) { - x2 += 1LL << i; - y2 += x2; - x2 /= 3 * (i % 7 ? -1 : 1); - y2 *= 5 * (i % 2 ? -1 : 1); - printf("signed %d: %lld,%lld,%lld,%lld,%lld,%lld,%lld,%lld,%lld\n", i, x2, y2, x2+y2, x2-y2, x2*y2, y2 ? x2/y2 : 0, x2 ? y2/x2 : 0, y2 ? x2%y2 : 0, x2 ? y2%x2 : 0); - } - return 0; - } - ''' - self.do_run(src, open(path_from_root('tests', 'i64_precise.txt')).read()) - - # Verify that even if we ask for precision, if it is not needed it is not included - Settings.PRECISE_I64_MATH = 1 - src = ''' - #include - #include - - int main(int argc, char **argv) { - uint64_t x = 2125299906845564, y = 1225891506842664; - if (argc == 12) { - x = x >> 1; - y = y >> 1; - } - x = x & 12ULL; - y = y | 12ULL; - x = x ^ y; - x <<= 2; - y >>= 3; - printf("*%llu, %llu*\\n", x, y); - } - ''' - self.do_run(src, '*4903566027370624, 153236438355333*') - code = open(os.path.join(self.get_dir(), 'src.cpp.o.js')).read() - assert 'goog.math.Long' not in code, 'i64 precise math should not have been included if not actually used' - - # But if we force it to be included, it is. First, a case where we don't need it - Settings.PRECISE_I64_MATH = 2 - self.do_run(open(path_from_root('tests', 'hello_world.c')).read(), 'hello') - code = open(os.path.join(self.get_dir(), 'src.cpp.o.js')).read() - assert 'goog.math.Long' in code, 'i64 precise math should be included if forced' - - # and now one where we do - self.do_run(r''' - #include - - int main( int argc, char ** argv ) - { - unsigned long a = 0x60DD1695U; - unsigned long b = 0xCA8C4E7BU; - unsigned long long c = (unsigned long long)a * b; - printf( "c = %016llx\n", c ); - - return 0; - } - ''', 'c = 4ca38a6bd2973f97') - - def test_i64_llabs(self): - if Settings.USE_TYPED_ARRAYS != 2: return self.skip('full i64 stuff only in ta2') - Settings.PRECISE_I64_MATH = 2 - self.do_run(r''' - #include - #include - - int main(int argc, char ** argv) { - printf("%lld,%lld\n", llabs(-576460752303423489), llabs(576460752303423489)); - return 0; - } - ''', '576460752303423489,576460752303423489') - - def test_i64_zextneg(self): - if Settings.USE_TYPED_ARRAYS != 2: return self.skip('full i64 stuff only in ta2') - - src = r''' - #include - #include - - int main(int argc, char *argv[]) - { - uint8_t byte = 0x80; - uint16_t two = byte; - uint32_t four = byte; - uint64_t eight = byte; - - printf("value: %d,%d,%d,%lld.\n", byte, two, four, eight); - - return 0; - } - ''' - self.do_run(src, 'value: 128,128,128,128.') - - def test_i64_7z(self): - if Settings.USE_TYPED_ARRAYS != 2: return self.skip('full i64 stuff only in ta2') - - src = r''' - #include - #include - uint64_t a, b; - int main(int argc, char *argv[]) - { - a = argc; - b = argv[1][0]; - printf("%d,%d\n", a, b); - if (a > a + b || a > a + b + 1) { - printf("one %lld, %lld", a, b); - return 0; - } - printf("zero %lld, %lld", a, b); - return 0; - } - ''' - self.do_run(src, 'zero 2, 104', ['hallo']) - - def test_i64_i16(self): - if Settings.USE_TYPED_ARRAYS != 2: return self.skip('full i64 stuff only in ta2') - - src = r''' - #include - #include - int main(int argc, char ** argv){ - int y=-133; - int64_t x= ((int64_t)((short)(y)))*(100 + argc); - if(x>0) - printf(">0\n"); - else - printf("<=0\n"); - } - ''' - self.do_run(src, '<=0') - - def test_i64_qdouble(self): - if Settings.USE_TYPED_ARRAYS != 2: return self.skip('full i64 stuff only in ta2') - - src = r''' - #include - typedef long long qint64; /* 64 bit signed */ - typedef double qreal; - - - int main(int argc, char **argv) - { - qreal c = 111; - qint64 d = -111 + (argc - 1); - c += d; - if (c < -1 || c > 1) - { - printf("Failed!\n"); - } - else - { - printf("Succeeded!\n"); - } - }; - ''' - self.do_run(src, 'Succeeded!') - - def test_i64_varargs(self): - if Settings.USE_TYPED_ARRAYS != 2: return self.skip('full i64 stuff only in ta2') - - src = r''' - #include - #include - #include - - int64_t ccv_cache_generate_signature(char *msg, int len, int64_t sig_start, ...) { - if (sig_start < 10123) - printf("%s\n", msg+len); - va_list v; - va_start(v, sig_start); - if (sig_start > 1413) - printf("%d\n", va_arg(v, int)); - else - printf("nada\n"); - va_end(v); - return len*sig_start*(msg[0]+1); + actualImage.src = actualUrl; } + img.src = '%s'; + }; + Module['postRun'] = doReftest; + Module['preRun'].push(function() { + setTimeout(doReftest, 1000); // if run() throws an exception and postRun is not called, this will kick in + }); +''' % basename) - int main(int argc, char **argv) - { - for (int i = 0; i < argc; i++) { - int64_t x; - if (i % 123123 == 0) - x = ccv_cache_generate_signature(argv[i], i+2, (int64_t)argc*argc, 54.111); - else - x = ccv_cache_generate_signature(argv[i], i+2, (int64_t)argc*argc, 13); - printf("%lld\n", x); - } - }; - ''' - self.do_run(src, '''in/this.program -nada -1536 -a -nada -5760 -fl -nada -6592 -sdfasdfasdf -nada -7840 -''', 'waka fleefl asdfasdfasdfasdf'.split(' ')) - - def test_i32_mul_precise(self): - if self.emcc_args == None: return self.skip('needs ta2') - - src = r''' - #include + def btest(self, filename, expected=None, reference=None, force_c=False, reference_slack=0, + args=[], outfile='test.html', message='.'): # TODO: use in all other tests + # if we are provided the source and not a path, use that + filename_is_src = '\n' in filename + src = filename if filename_is_src else '' + filepath = path_from_root('tests', filename) if not filename_is_src else ('main.c' if force_c else 'main.cpp') + temp_filepath = os.path.join(self.get_dir(), os.path.basename(filepath)) + if filename_is_src: + with open(temp_filepath, 'w') as f: f.write(src) + if not reference: + if not src: + with open(filepath) as f: src = f.read() + with open(temp_filepath, 'w') as f: f.write(self.with_report_result(src)) + else: + expected = [str(i) for i in range(0, reference_slack+1)] + shutil.copyfile(filepath, temp_filepath) + self.reftest(path_from_root('tests', reference)) + args = args + ['--pre-js', 'reftest.js', '-s', 'GL_TESTING=1'] + Popen([PYTHON, EMCC, temp_filepath, '-o', outfile] + args).communicate() + if type(expected) is str: expected = [expected] + self.run_browser(outfile, message, ['/report_result?' + e for e in expected]) - int main(int argc, char **argv) { - unsigned long d1 = 0x847c9b5d; - unsigned long q = 0x549530e1; - if (argc > 1000) { q += argc; d1 -= argc; } // confuse optimizer - printf("%lu\n", d1*q); - return 0; - } - ''' - self.do_run(src, '3217489085') +################################################################################################### - def test_i32_mul_semiprecise(self): - if Settings.ASM_JS: return self.skip('asm is always fully precise') +if __name__ == '__main__': + # The runner module is imported by the tests, add the current module + # instance into the cache so global variables such as js_engine_override + # are shared correctly with the test suite modules. + sys.modules['runner'] = sys.modules[__name__] - Settings.PRECISE_I32_MUL = 0 # we want semiprecise here + # Sanity checks + total_engines = len(JS_ENGINES) + JS_ENGINES = filter(check_engine, JS_ENGINES) + if len(JS_ENGINES) == 0: + print 'WARNING: None of the JS engines in JS_ENGINES appears to work.' + elif len(JS_ENGINES) < total_engines: + print 'WARNING: Not all the JS engines in JS_ENGINES appears to work, ignoring those.' - src = r''' - #include + # If an argument comes in as test_*, treat it as a test of the default suite + sys.argv = map(lambda arg: arg if not arg.startswith('test_') else 'default.' + arg, sys.argv) - typedef unsigned int uint; + # If no tests were specified, run the core suite + if len(sys.argv) == 1: + sys.argv = [sys.argv[0]] + map(lambda mode: mode, test_modes) + print ''' +============================================================================== +Running the main part of the test suite. Don't forget to run the other parts! - // from cube2, zlib licensed + sanity - tests for first run, etc., modifies ~/.emscripten + benchmark - run before and after each set of changes before pushing to + master, verify no regressions + browser - runs pages in a web browser + sockets - runs websocket networking tests - #define N (624) - #define M (397) - #define K (0x9908B0DFU) +There are also commands to run specific subsets of the test suite: - static uint state[N]; - static int next = N; + browser.audio - runs audio tests in a web browser (requires human verification) - void seedMT(uint seed) - { - state[0] = seed; - for(uint i = 1; i < N; i++) // if we do not do this precisely, at least we should coerce to int immediately, not wait - state[i] = seed = 1812433253U * (seed ^ (seed >> 30)) + i; - next = 0; - } +To run one of those parts, do something like - int main() { - seedMT(5497); - for (int i = 0; i < 10; i++) printf("%d: %u\n", i, state[i]); - return 0; - } - ''' - self.do_run(src, '''0: 5497 -1: 2916432318 -2: 2502517762 -3: 3151524867 -4: 2323729668 -5: 2053478917 -6: 2409490438 -7: 848473607 -8: 691103752 -9: 3915535113 -''') + python tests/runner.py sanity - def test_i16_emcc_intrinsic(self): - Settings.CORRECT_SIGNS = 1 # Relevant to this test +To run a specific set of tests, you can do things like - src = r''' - #include + python tests/runner.py o1 - int test(unsigned short a, unsigned short b) { - unsigned short result = a; - result += b; - if (result < b) printf("C!"); - return result; - } +(that runs the o1 (-O1) tests). You can run individual tests with - int main(void) { - printf(",%d,", test(0, 0)); - printf(",%d,", test(1, 1)); - printf(",%d,", test(65535, 1)); - printf(",%d,", test(1, 65535)); - printf(",%d,", test(32768, 32767)); - printf(",%d,", test(32768, 32768)); - return 0; - } - ''' - self.do_run(src, ',0,,2,C!,0,C!,0,,65535,C!,0,') + python tests/runner.py test_hello_world - def test_negative_zero(self): - src = r''' - #include - #include +Combinations work too, for example - int main() { - #define TEST(x, y) \ - printf("%.2f, %.2f ==> %.2f\n", x, y, copysign(x, y)); - TEST( 5.0f, 5.0f); - TEST( 5.0f, -5.0f); - TEST(-5.0f, 5.0f); - TEST(-5.0f, -5.0f); - TEST( 5.0f, 4.0f); - TEST( 5.0f, -4.0f); - TEST(-5.0f, 4.0f); - TEST(-5.0f, -4.0f); - TEST( 0.0f, 5.0f); - TEST( 0.0f, -5.0f); - TEST(-0.0f, 5.0f); - TEST(-0.0f, -5.0f); - TEST( 5.0f, 0.0f); - TEST( 5.0f, -0.0f); - TEST(-5.0f, 0.0f); - TEST(-5.0f, -0.0f); - TEST( 0.0f, 0.0f); - TEST( 0.0f, -0.0f); - TEST(-0.0f, 0.0f); - TEST(-0.0f, -0.0f); - return 0; - } - ''' - self.do_run(src, '''5.00, 5.00 ==> 5.00 -5.00, -5.00 ==> -5.00 --5.00, 5.00 ==> 5.00 --5.00, -5.00 ==> -5.00 -5.00, 4.00 ==> 5.00 -5.00, -4.00 ==> -5.00 --5.00, 4.00 ==> 5.00 --5.00, -4.00 ==> -5.00 -0.00, 5.00 ==> 0.00 -0.00, -5.00 ==> -0.00 --0.00, 5.00 ==> 0.00 --0.00, -5.00 ==> -0.00 -5.00, 0.00 ==> 5.00 -5.00, -0.00 ==> -5.00 --5.00, 0.00 ==> 5.00 --5.00, -0.00 ==> -5.00 -0.00, 0.00 ==> 0.00 -0.00, -0.00 ==> -0.00 --0.00, 0.00 ==> 0.00 --0.00, -0.00 ==> -0.00 -''') + python tests/runner.py browser.test_sdl_image - def test_llvm_intrinsics(self): - if self.emcc_args == None: return self.skip('needs ta2') +In the main test suite, you can run all variations (O0, O1, O2, etc.) of +an individual test with - Settings.PRECISE_I64_MATH = 2 # for bswap64 + python tests/runner.py ALL.test_hello_world - src = r''' - #include - #include +============================================================================== - extern "C" { - extern unsigned short llvm_bswap_i16(unsigned short x); - extern unsigned int llvm_bswap_i32(unsigned int x); - extern int32_t llvm_ctlz_i32(int32_t x); - extern int64_t llvm_ctlz_i64(int64_t x); - extern int32_t llvm_cttz_i32(int32_t x); - extern int64_t llvm_cttz_i64(int64_t x); - extern int32_t llvm_ctpop_i32(int32_t x); - extern int64_t llvm_ctpop_i64(int64_t x); - extern int llvm_expect_i32(int x, int y); - } +''' + time.sleep(2) - int main(void) { - unsigned short x = 0xc8ef; - printf("%x,%x\n", x&0xff, x >> 8); - x = llvm_bswap_i16(x); - printf("%x,%x\n", x&0xff, x >> 8); + # If a test (e.g. test_html) is specified as ALL.test_html, add an entry for each test_mode + if len(sys.argv) == 2 and sys.argv[1].startswith('ALL.'): + ignore, test = sys.argv[1].split('.') + print 'Running all test modes on test "%s"' % test + sys.argv = [sys.argv[0]] + map(lambda mode: mode+'.'+test, test_modes) - unsigned int y = 0xc5de158a; - printf("%x,%x,%x,%x\n", y&0xff, (y>>8)&0xff, (y>>16)&0xff, (y>>24)&0xff); - y = llvm_bswap_i32(y); - printf("%x,%x,%x,%x\n", y&0xff, (y>>8)&0xff, (y>>16)&0xff, (y>>24)&0xff); + # Skip requested tests + for i in range(len(sys.argv)): + arg = sys.argv[i] + if arg.startswith('skip:'): + which = arg.split('skip:')[1] + if which.startswith('ALL.'): + ignore, test = which.split('.') + which = map(lambda mode: mode+'.'+test, test_modes) + else: + which = [which] - printf("%d,%d\n", (int)llvm_ctlz_i64(((int64_t)1) << 40), llvm_ctlz_i32(1<<10)); - printf("%d,%d\n", (int)llvm_cttz_i64(((int64_t)1) << 40), llvm_cttz_i32(1<<10)); - printf("%d,%d\n", (int)llvm_ctpop_i64((0x3101ULL << 32) | 1), llvm_ctpop_i32(0x3101)); - printf("%d\n", (int)llvm_ctpop_i32(-594093059)); + print >> sys.stderr, ','.join(which) + for test in which: + print >> sys.stderr, 'will skip "%s"' % test + exec(test + ' = RunnerCore.skipme') - printf("%d\n", llvm_expect_i32(x % 27, 3)); + sys.argv[i] = '' + sys.argv = filter(lambda arg: arg, sys.argv) - int64_t a = 1; - a = __builtin_bswap64(a); - printf("%lld\n", a); + # Extract the JS engine override from the arguments (used by benchmarks) + for i in range(1, len(sys.argv)): + arg = sys.argv[i] + if arg.isupper(): + print 'Interpreting all capital argument "%s" as JS_ENGINE override' % arg + js_engine_override = eval(arg) + sys.argv[i] = None + sys.argv = filter(lambda arg: arg is not None, sys.argv) - return 0; - } - ''' - self.do_run(src, '''ef,c8 -c8,ef -8a,15,de,c5 -c5,de,15,8a -23,21 -40,10 -5,4 -22 -13 -72057594037927936 -''') - - def test_bswap64(self): - if Settings.USE_TYPED_ARRAYS != 2: return self.skip('needs ta2') - - src = r''' - #include - #include - - #include - #include - #include - - typedef unsigned long long quint64; - - using namespace std; - - inline quint64 qbswap(quint64 source) - { - return 0 - | ((source & quint64(0x00000000000000ffLL)) << 56) - | ((source & quint64(0x000000000000ff00LL)) << 40) - | ((source & quint64(0x0000000000ff0000LL)) << 24) - | ((source & quint64(0x00000000ff000000LL)) << 8) - | ((source & quint64(0x000000ff00000000LL)) >> 8) - | ((source & quint64(0x0000ff0000000000LL)) >> 24) - | ((source & quint64(0x00ff000000000000LL)) >> 40) - | ((source & quint64(0xff00000000000000LL)) >> 56); - } - - int main() - { - quint64 v = strtoull("4433ffeeddccbb00", NULL, 16); - printf("%lld\n", v); - - const string string64bitInt = "4433ffeeddccbb00"; - stringstream s(string64bitInt); - quint64 int64bitInt = 0; - printf("1\n"); - s >> hex >> int64bitInt; - printf("2\n"); - - stringstream out; - out << hex << qbswap(int64bitInt); - - cout << out.str() << endl; - cout << hex << int64bitInt << endl; - cout << string64bitInt << endl; - - if (out.str() != "bbccddeeff3344") - { - cout << "Failed!" << endl; - } - else - { - cout << "Succeeded!" << endl; - } - - return 0; - } - ''' - self.do_run(src, '''4914553019779824384 -1 -2 -bbccddeeff3344 -4433ffeeddccbb00 -4433ffeeddccbb00 -Succeeded! -''') - - def test_sha1(self): - if self.emcc_args == None: return self.skip('needs ta2') - - self.do_run(open(path_from_root('tests', 'sha1.c')).read(), 'SHA1=15dd99a1991e0b3826fede3deffc1feba42278e6') - - def test_cube2md5(self): - if self.emcc_args == None: return self.skip('needs emcc') - self.emcc_args += ['--embed-file', 'cube2md5.txt'] - shutil.copyfile(path_from_root('tests', 'cube2md5.txt'), os.path.join(self.get_dir(), 'cube2md5.txt')) - self.do_run(open(path_from_root('tests', 'cube2md5.cpp')).read(), open(path_from_root('tests', 'cube2md5.ok')).read()) - - def test_cube2hash(self): - - try: - old_chunk_size = os.environ.get('EMSCRIPT_MAX_CHUNK_SIZE') or '' - os.environ['EMSCRIPT_MAX_CHUNK_SIZE'] = '1' # test splitting out each function to a chunk in emscripten.py (21 functions here) - - # A good test of i64 math - if Settings.USE_TYPED_ARRAYS != 2: return self.skip('requires ta2 C-style memory aliasing') - self.do_run('', 'Usage: hashstring ', - libraries=self.get_library('cube2hash', ['cube2hash.bc'], configure=None), - includes=[path_from_root('tests', 'cube2hash')]) - - for text, output in [('fleefl', '892BDB6FD3F62E863D63DA55851700FDE3ACF30204798CE9'), - ('fleefl2', 'AA2CC5F96FC9D540CA24FDAF1F71E2942753DB83E8A81B61'), - ('64bitisslow', '64D8470573635EC354FEE7B7F87C566FCAF1EFB491041670')]: - self.do_run('', 'hash value: ' + output, [text], no_build=True) - finally: - os.environ['EMSCRIPT_MAX_CHUNK_SIZE'] = old_chunk_size - - def test_unaligned(self): - if Settings.QUANTUM_SIZE == 1: return self.skip('No meaning to unaligned addresses in q1') - - src = r''' - #include - - struct S { - double x; - int y; - }; - - int main() { - // the 64-bit value here will not be 8-byte aligned - S s0[3] = { {0x12a751f430142, 22}, {0x17a5c85bad144, 98}, {1, 1}}; - char buffer[10*sizeof(S)]; - int b = int(buffer); - S *s = (S*)(b + 4-b%8); - s[0] = s0[0]; - s[1] = s0[1]; - s[2] = s0[2]; - - printf("*%d : %d : %d\n", sizeof(S), ((unsigned int)&s[0]) % 8 != ((unsigned int)&s[1]) % 8, - ((unsigned int)&s[1]) - ((unsigned int)&s[0])); - s[0].x++; - s[0].y++; - s[1].x++; - s[1].y++; - printf("%.1f,%d,%.1f,%d\n", s[0].x, s[0].y, s[1].x, s[1].y); - return 0; - } - ''' - - # TODO: A version of this with int64s as well - - if self.is_le32(): - return self.skip('LLVM marks the reads of s as fully aligned, making this test invalid') - else: - self.do_run(src, '*12 : 1 : 12\n328157500735811.0,23,416012775903557.0,99\n') - - return # TODO: continue to the next part here - - # Test for undefined behavior in C. This is not legitimate code, but does exist - - if Settings.USE_TYPED_ARRAYS != 2: return self.skip('No meaning to unaligned addresses without t2') - - src = r''' - #include - - int main() - { - int x[10]; - char *p = (char*)&x[0]; - p++; - short *q = (short*)p; - *q = 300; - printf("*%d:%d*\n", *q, ((int)q)%2); - int *r = (int*)p; - *r = 515559; - printf("*%d*\n", *r); - long long *t = (long long*)p; - *t = 42949672960; - printf("*%Ld*\n", *t); - return 0; - } - ''' - - try: - self.do_run(src, '*300:1*\n*515559*\n*42949672960*\n') - except Exception, e: - assert 'must be aligned' in str(e), e # expected to fail without emulation - - def test_align64(self): - src = r''' - #include - - // inspired by poppler - - enum Type { - A = 10, - B = 20 - }; - - struct Object { - Type type; - union { - int intg; - double real; - char *name; - }; - }; - - struct Principal { - double x; - Object a; - double y; - }; - - int main(int argc, char **argv) - { - int base = argc-1; - Object *o = NULL; - printf("%d,%d\n", sizeof(Object), sizeof(Principal)); - printf("%d,%d,%d,%d\n", (int)&o[base].type, (int)&o[base].intg, (int)&o[base].real, (int)&o[base].name); - printf("%d,%d,%d,%d\n", (int)&o[base+1].type, (int)&o[base+1].intg, (int)&o[base+1].real, (int)&o[base+1].name); - Principal p, q; - p.x = p.y = q.x = q.y = 0; - p.a.type = A; - p.a.real = 123.456; - *(&q.a) = p.a; - printf("%.2f,%d,%.2f,%.2f : %.2f,%d,%.2f,%.2f\n", p.x, p.a.type, p.a.real, p.y, q.x, q.a.type, q.a.real, q.y); - return 0; - } - ''' - - if self.is_le32(): - self.do_run(src, '''16,32 -0,8,8,8 -16,24,24,24 -0.00,10,123.46,0.00 : 0.00,10,123.46,0.00 -''') - else: - self.do_run(src, '''12,28 -0,4,4,4 -12,16,16,16 -0.00,10,123.46,0.00 : 0.00,10,123.46,0.00 -''') - - def test_unsigned(self): - Settings.CORRECT_SIGNS = 1 # We test for exactly this sort of thing here - Settings.CHECK_SIGNS = 0 - src = ''' - #include - const signed char cvals[2] = { -1, -2 }; // compiler can store this is a string, so -1 becomes \FF, and needs re-signing - int main() - { - { - unsigned char x = 200; - printf("*%d*\\n", x); - unsigned char y = -22; - printf("*%d*\\n", y); - } - - int varey = 100; - unsigned int MAXEY = -1, MAXEY2 = -77; - printf("*%u,%d,%u*\\n", MAXEY, varey >= MAXEY, MAXEY2); // 100 >= -1? not in unsigned! - - int y = cvals[0]; - printf("*%d,%d,%d,%d*\\n", cvals[0], cvals[0] < 0, y, y < 0); - y = cvals[1]; - printf("*%d,%d,%d,%d*\\n", cvals[1], cvals[1] < 0, y, y < 0); - - // zext issue - see mathop in jsifier - unsigned char x8 = -10; - unsigned long hold = 0; - hold += x8; - int y32 = hold+50; - printf("*%u,%u*\\n", hold, y32); - - // Comparisons - x8 = 0; - for (int i = 0; i < 254; i++) x8++; // make it an actual 254 in JS - not a -2 - printf("*%d,%d*\\n", x8+1 == 0xff, x8+1 != 0xff); // 0xff may be '-1' in the bitcode - - return 0; - } - ''' - self.do_run(src, '*4294967295,0,4294967219*\n*-1,1,-1,1*\n*-2,1,-2,1*\n*246,296*\n*1,0*') - - # Now let's see some code that should just work in USE_TYPED_ARRAYS == 2, but requires - # corrections otherwise - if Settings.USE_TYPED_ARRAYS == 2: - Settings.CORRECT_SIGNS = 0 - Settings.CHECK_SIGNS = 1 if not Settings.ASM_JS else 0 - else: - Settings.CORRECT_SIGNS = 1 - Settings.CHECK_SIGNS = 0 - - src = ''' - #include - int main() - { - { - unsigned char x; - unsigned char *y = &x; - *y = -1; - printf("*%d*\\n", x); - } - { - unsigned short x; - unsigned short *y = &x; - *y = -1; - printf("*%d*\\n", x); - } - /*{ // This case is not checked. The hint for unsignedness is just the %u in printf, and we do not analyze that - unsigned int x; - unsigned int *y = &x; - *y = -1; - printf("*%u*\\n", x); - }*/ - { - char x; - char *y = &x; - *y = 255; - printf("*%d*\\n", x); - } - { - char x; - char *y = &x; - *y = 65535; - printf("*%d*\\n", x); - } - { - char x; - char *y = &x; - *y = 0xffffffff; - printf("*%d*\\n", x); - } - return 0; - } - ''' - self.do_run(src, '*255*\n*65535*\n*-1*\n*-1*\n*-1*') - - def test_bitfields(self): - if self.emcc_args is None: Settings.SAFE_HEAP = 0 # bitfields do loads on invalid areas, by design - src = ''' - #include - struct bitty { - unsigned x : 1; - unsigned y : 1; - unsigned z : 1; - }; - int main() - { - bitty b; - printf("*"); - for (int i = 0; i <= 1; i++) - for (int j = 0; j <= 1; j++) - for (int k = 0; k <= 1; k++) { - b.x = i; - b.y = j; - b.z = k; - printf("%d,%d,%d,", b.x, b.y, b.z); - } - printf("*\\n"); - return 0; - } - ''' - self.do_run(src, '*0,0,0,0,0,1,0,1,0,0,1,1,1,0,0,1,0,1,1,1,0,1,1,1,*') - - def test_floatvars(self): - src = ''' - #include - - // headers test, see issue #1013 - #include - #include - - int main(int argc, char **argv) - { - float x = 1.234, y = 3.5, q = 0.00000001; - y *= 3; - int z = x < y; - printf("*%d,%d,%.1f,%d,%.4f,%.2f*\\n", z, int(y), y, (int)x, x, q); - - printf("%.2f, %.2f, %.2f, %.2f\\n", fmin(0.5, 3.3), fmin(NAN, 3.3), fmax(0.5, 3.3), fmax(NAN, 3.3)); - - printf("small: %.10f\\n", argc * 0.000001); - - /* - // Rounding behavior - float fs[6] = { -2.75, -2.50, -2.25, 2.25, 2.50, 2.75 }; - double ds[6] = { -2.75, -2.50, -2.25, 2.25, 2.50, 2.75 }; - for (int i = 0; i < 6; i++) - printf("*int(%.2f)=%d,%d*\\n", fs[i], int(fs[i]), int(ds[i])); - */ - - return 0; - } - ''' - self.do_run(src, '*1,10,10.5,1,1.2340,0.00*\n0.50, 3.30, 3.30, 3.30\nsmall: 0.0000010000\n') - - def test_isnan(self): - src = r''' - #include - - int IsNaN(double x){ - int rc; /* The value return */ - volatile double y = x; - volatile double z = y; - rc = (y!=z); - return rc; - } - - int main() { - double tests[] = { 1.0, 3.333, 1.0/0.0, 0.0/0.0, -1.0/0.0, -0, 0, -123123123, 12.0E200 }; - for (int i = 0; i < sizeof(tests)/sizeof(double); i++) - printf("%d - %f - %d\n", i, tests[i], IsNaN(tests[i])); - } - ''' - self.do_run(src, '''0 - 1.000000 - 0 -1 - 3.333000 - 0 -2 - inf - 0 -3 - nan - 1 -4 - -inf - 0 -5 - 0.000000 - 0 -6 - 0.000000 - 0 -7 - -123123123.000000 - 0 -8 - 1.2e+201 - 0 -''') - - def test_globaldoubles(self): - src = r''' - #include - #include - - double testVu, testVv, testWu, testWv; - - void Test(double _testVu, double _testVv, double _testWu, double _testWv) - { - testVu = _testVu; - testVv = _testVv; - testWu = _testWu; - testWv = _testWv; - printf("BUG?\n"); - printf("Display: Vu=%f Vv=%f Wu=%f Wv=%f\n", testVu, testVv, testWu, testWv); - } - - int main(void) - { - double v1 = 465.1; - double v2 = 465.2; - double v3 = 160.3; - double v4 = 111.4; - Test(v1, v2, v3, v4); - return 0; - } - ''' - self.do_run(src, 'BUG?\nDisplay: Vu=465.100000 Vv=465.200000 Wu=160.300000 Wv=111.400000') - - def test_math(self): - src = ''' - #include - #include - #include - int main() - { - printf("*%.2f,%.2f,%d", M_PI, -M_PI, (1/0.0) > 1e300); // could end up as infinity, or just a very very big number - printf(",%d", isfinite(NAN) != 0); - printf(",%d", isfinite(INFINITY) != 0); - printf(",%d", isfinite(-INFINITY) != 0); - printf(",%d", isfinite(12.3) != 0); - printf(",%d", isinf(NAN) != 0); - printf(",%d", isinf(INFINITY) != 0); - printf(",%d", isinf(-INFINITY) != 0); - printf(",%d", isinf(12.3) != 0); - div_t div_result = div(23, 10); - printf(",%d", div_result.quot); - printf(",%d", div_result.rem); - double sine = -1.0, cosine = -1.0; - sincos(0.0, &sine, &cosine); - printf(",%1.1lf", sine); - printf(",%1.1lf", cosine); - float fsine = -1.0f, fcosine = -1.0f; - sincosf(0.0, &fsine, &fcosine); - printf(",%1.1f", fsine); - printf(",%1.1f", fcosine); - printf("*\\n"); - return 0; - } - ''' - self.do_run(src, '*3.14,-3.14,1,0,0,0,1,0,1,1,0,2,3,0.0,1.0,0.0,1.0*') - - def test_erf(self): - src = ''' - #include - #include - int main() - { - printf("%1.6f, %1.6f, %1.6f, %1.6f, %1.6f, %1.6f\\n", - erf(1.0), - erf(3.0), - erf(-1.0), - erfc(1.0), - erfc(3.0), - erfc(-1.5)); - return 0; - } - ''' - self.do_run(src, '0.842701, 0.999978, -0.842701, 0.157299, 0.000022, 1.966105') - - def test_math_hyperbolic(self): - src = open(path_from_root('tests', 'hyperbolic', 'src.c'), 'r').read() - expected = open(path_from_root('tests', 'hyperbolic', 'output.txt'), 'r').read() - self.do_run(src, expected) - - def test_frexp(self): - src = ''' - #include - #include - #include - - static const double tol=1e-16; - - void test_value(double value) - { - int exponent; - double x=frexp(value, &exponent); - double expected=x*pow(2.0, exponent); - - printf("%f=%f*2^%d\\n", value, x, exponent); - - assert(fabs(expected-value)=5e-1 && fabs(x)<1)); // x has a magnitude in the interval [1/2, 1) - } - - int main() - { - test_value(0); - test_value(100.1); - test_value(-100.1); - test_value(.5); - test_value(-.5); - test_value(1-1e-16); - test_value(-(1-1e-16)); - - return 0; - } - ''' - self.do_run(src, '''0.000000=0.000000*2^0 -100.100000=0.782031*2^7 --100.100000=-0.782031*2^7 -0.500000=0.500000*2^0 --0.500000=-0.500000*2^0 -1.000000=1.000000*2^0 --1.000000=-1.000000*2^0''') - - def test_rounding(self): - src = ''' - #include - #include - - int main() - { - printf("%.1f ", round(1.4)); - printf("%.1f ", round(1.6)); - printf("%.1f ", round(-1.4)); - printf("%.1f ", round(-1.6)); - - printf("%.1f ", round(1.5)); - printf("%.1f ", round(2.5)); - printf("%.1f ", round(-1.5)); - printf("%.1f ", round(-2.5)); - - printf("%ld ", lrint(1.4)); - printf("%ld ", lrint(1.6)); - printf("%ld ", lrint(-1.4)); - printf("%ld ", lrint(-1.6)); - - printf("%ld ", lrint(1.5)); - printf("%ld ", lrint(2.5)); - printf("%ld ", lrint(-1.5)); - printf("%ld ", lrint(-2.5)); - - return 0; - } - ''' - self.do_run(src, "1.0 2.0 -1.0 -2.0 2.0 3.0 -2.0 -3.0 " - "1 2 -1 -2 2 2 -2 -2") - - # This example borrowed from MSDN documentation - def test_fcvt(self): - if self.emcc_args is None: return self.skip('requires emcc') - - src = ''' - #include - #include - - int main() { - int decimal, sign; - char *buffer; - double source = 3.1415926535; - - buffer = fcvt(source, 7, &decimal, &sign); - printf("source: %2.10f buffer: '%s' decimal: %d sign: %d\\n", - source, buffer, decimal, sign); - } - ''' - self.do_run(src, "source: 3.1415926535 buffer: '31415927' decimal: 1 sign: 0"); - - def test_llrint(self): - if Settings.USE_TYPED_ARRAYS != 2: return self.skip('requires ta2') - src = r''' - #include - #include - int main() { - printf("%lld\n%lld\n%lld\n%lld\n", llrint(0.1), llrint(0.6), llrint(1.25), llrint(1099511627776.667)); - return 0; - } - ''' - self.do_run(src, '0\n1\n1\n1099511627777\n') - - def test_getgep(self): - # Generated code includes getelementptr (getelementptr, 0, 1), i.e., GEP as the first param to GEP - src = ''' - #include - struct { - int y[10]; - int z[10]; - } commonblock; - - int main() - { - for (int i = 0; i < 10; ++i) { - commonblock.y[i] = 1; - commonblock.z[i] = 2; - } - printf("*%d %d*\\n", commonblock.y[0], commonblock.z[0]); - return 0; - } - ''' - self.do_run(src, '*1 2*') - - def test_multiply_defined_symbols(self): - a1 = "int f() { return 1; }" - a1_name = os.path.join(self.get_dir(), 'a1.c') - open(a1_name, 'w').write(a1) - a2 = "void x() {}" - a2_name = os.path.join(self.get_dir(), 'a2.c') - open(a2_name, 'w').write(a2) - b1 = "int f() { return 2; }" - b1_name = os.path.join(self.get_dir(), 'b1.c') - open(b1_name, 'w').write(b1) - b2 = "void y() {}" - b2_name = os.path.join(self.get_dir(), 'b2.c') - open(b2_name, 'w').write(b2) - main = r''' - #include - int f(); - int main() { - printf("result: %d\n", f()); - return 0; - } - ''' - main_name = os.path.join(self.get_dir(), 'main.c') - open(main_name, 'w').write(main) - - Building.emcc(a1_name) - Building.emcc(a2_name) - Building.emcc(b1_name) - Building.emcc(b2_name) - Building.emcc(main_name) - - liba_name = os.path.join(self.get_dir(), 'liba.a') - Building.emar('cr', liba_name, [a1_name + '.o', a2_name + '.o']) - libb_name = os.path.join(self.get_dir(), 'libb.a') - Building.emar('cr', libb_name, [b1_name + '.o', b2_name + '.o']) - - all_name = os.path.join(self.get_dir(), 'all.bc') - Building.link([main_name + '.o', liba_name, libb_name], all_name) - - self.do_ll_run(all_name, 'result: 1') - - def test_if(self): - src = ''' - #include - int main() - { - int x = 5; - if (x > 3) { - printf("*yes*\\n"); - } - return 0; - } - ''' - self.do_run(src, '*yes*') - - def test_if_else(self): - src = ''' - #include - int main() - { - int x = 5; - if (x > 10) { - printf("*yes*\\n"); - } else { - printf("*no*\\n"); - } - return 0; - } - ''' - self.do_run(src, '*no*') - - def test_loop(self): - src = ''' - #include - int main() - { - int x = 5; - for (int i = 0; i < 6; i++) { - x += x*i; - if (x > 1000) { - if (x % 7 == 0) printf("cheez\\n"); - x /= 2; - break; - } - } - printf("*%d*\\n", x); - return 0; - } - ''' - - self.do_run(src, '*1800*') - - generated = open('src.cpp.o.js', 'r').read() - - def test_stack(self): - Settings.INLINING_LIMIT = 50 - - src = ''' - #include - int test(int i) { - int x = 10; - if (i > 0) { - return test(i-1); - } - return int(&x); // both for the number, and forces x to not be nativized - } - int main(int argc, char **argv) - { - // We should get the same value for the first and last - stack has unwound - int x1 = test(argc - 2); - int x2 = test(100); - int x3 = test((argc - 2) / 4); - printf("*%d,%d*\\n", x3-x1, x2 != x1); - return 0; - } - ''' - self.do_run(src, '*0,1*') - - def test_strings(self): - src = ''' - #include - #include - #include - - int main(int argc, char **argv) - { - int x = 5, y = 9, magic = 7; // fool compiler with magic - memmove(&x, &y, magic-7); // 0 should not crash us - - int xx, yy, zz; - char s[32]; - int cc = sscanf("abc_10.b1_xyz_543_defg", "abc_%d.%2x_xyz_%3d_%3s", &xx, &yy, &zz, s); - printf("%d:%d,%d,%d,%s\\n", cc, xx, yy, zz, s); - - printf("%d\\n", argc); - puts(argv[1]); - puts(argv[2]); - printf("%d\\n", atoi(argv[3])+2); - const char *foolingthecompiler = "\\rabcd"; - printf("%d\\n", strlen(foolingthecompiler)); // Tests parsing /0D in llvm - should not be a 0 (end string) then a D! - printf("%s\\n", NULL); // Should print '(null)', not the string at address 0, which is a real address for us! - printf("/* a comment */\\n"); // Should not break the generated code! - printf("// another\\n"); // Should not break the generated code! - - char* strdup_val = strdup("test"); - printf("%s\\n", strdup_val); - free(strdup_val); - - { - char *one = "one 1 ONE !"; - char *two = "two 2 TWO ?"; - char three[1024]; - memset(three, '.', 1024); - three[50] = 0; - strncpy(three + argc, one + (argc/2), argc+1); - strncpy(three + argc*3, two + (argc/3), argc+2); - printf("waka %s\\n", three); - } - - { - char *one = "string number one top notch"; - char *two = "fa la sa ho fi FI FO FUM WHEN WHERE WHY HOW WHO"; - char three[1000]; - strcpy(three, &one[argc*2]); - char *four = strcat(three, &two[argc*3]); - printf("cat |%s|\\n", three); - printf("returned |%s|\\n", four); - } - - return 0; - } - ''' - for named in (0, 1): - print named - Settings.NAMED_GLOBALS = named - self.do_run(src, '''4:10,177,543,def\n4\nwowie\ntoo\n76\n5\n(null)\n/* a comment */\n// another\ntest\nwaka ....e 1 O...wo 2 T................................ -cat |umber one top notchfi FI FO FUM WHEN WHERE WHY HOW WHO| -returned |umber one top notchfi FI FO FUM WHEN WHERE WHY HOW WHO|''', ['wowie', 'too', '74']) - if self.emcc_args == []: - gen = open(self.in_dir('src.cpp.o.js')).read() - assert ('var __str1;' in gen) == named - - def test_strcmp_uni(self): - src = ''' - #include - #include - int main() - { - #define TEST(func) \ - { \ - char *word = "WORD"; \ - char wordEntry[2] = { -61,-126 }; /* "Â"; */ \ - int cmp = func(word, wordEntry, 2); \ - printf("Compare value " #func " is %d\\n", cmp); \ - } - TEST(strncmp); - TEST(strncasecmp); - TEST(memcmp); - } - ''' - self.do_run(src, 'Compare value strncmp is -1\nCompare value strncasecmp is -1\nCompare value memcmp is -1\n') - - def test_strndup(self): - src = ''' - //--------------- - //- http://pubs.opengroup.org/onlinepubs/9699919799/functions/strndup.html - //--------------- - - #include - #include - #include - - int main(int argc, char **argv) { - const char* source = "strndup - duplicate a specific number of bytes from a string"; - - char* strdup_val = strndup(source, 0); - printf("1:%s\\n", strdup_val); - free(strdup_val); - - strdup_val = strndup(source, 7); - printf("2:%s\\n", strdup_val); - free(strdup_val); - - strdup_val = strndup(source, 1000); - printf("3:%s\\n", strdup_val); - free(strdup_val); - - strdup_val = strndup(source, 60); - printf("4:%s\\n", strdup_val); - free(strdup_val); - - strdup_val = strndup(source, 19); - printf("5:%s\\n", strdup_val); - free(strdup_val); - - strdup_val = strndup(source, -1); - printf("6:%s\\n", strdup_val); - free(strdup_val); - - return 0; - } - ''' - self.do_run(src, '1:\n2:strndup\n3:strndup - duplicate a specific number of bytes from a string\n4:strndup - duplicate a specific number of bytes from a string\n5:strndup - duplicate\n6:\n') - - def test_errar(self): - src = r''' - #include - #include - #include - - int main() { - char* err; - char buffer[200]; - - err = strerror(EDOM); - strerror_r(EWOULDBLOCK, buffer, 200); - printf("<%s>\n", err); - printf("<%s>\n", buffer); - - printf("<%d>\n", strerror_r(EWOULDBLOCK, buffer, 0)); - errno = 123; - printf("<%d>\n", errno); - - return 0; - } - ''' - expected = ''' - - - <34> - <123> - ''' - self.do_run(src, re.sub('(^|\n)\s+', '\\1', expected)) - - def test_mainenv(self): - src = ''' - #include - int main(int argc, char **argv, char **envp) - { - printf("*%p*\\n", envp); - return 0; - } - ''' - self.do_run(src, '*(nil)*') - - def test_funcs(self): - src = ''' - #include - int funcy(int x) - { - return x*9; - } - int main() - { - printf("*%d,%d*\\n", funcy(8), funcy(10)); - return 0; - } - ''' - self.do_run(src, '*72,90*') - - def test_structs(self): - src = ''' - #include - struct S - { - int x, y; - }; - int main() - { - S a, b; - a.x = 5; a.y = 6; - b.x = 101; b.y = 7009; - S *c, *d; - c = &a; - c->x *= 2; - c = &b; - c->y -= 1; - d = c; - d->y += 10; - printf("*%d,%d,%d,%d,%d,%d,%d,%d*\\n", a.x, a.y, b.x, b.y, c->x, c->y, d->x, d->y); - return 0; - } - ''' - self.do_run(src, '*10,6,101,7018,101,7018,101,7018*') - - gen_struct_src = ''' - #include - #include - #include "emscripten.h" - - struct S - { - int x, y; - }; - int main() - { - S* a = {{gen_struct}}; - a->x = 51; a->y = 62; - printf("*%d,%d*\\n", a->x, a->y); - {{del_struct}}(a); - return 0; - } - ''' - - def test_mallocstruct(self): - self.do_run(self.gen_struct_src.replace('{{gen_struct}}', '(S*)malloc(sizeof(S))').replace('{{del_struct}}', 'free'), '*51,62*') - - def test_newstruct(self): - if self.emcc_args is None: return self.skip('requires emcc') - self.do_run(self.gen_struct_src.replace('{{gen_struct}}', 'new S').replace('{{del_struct}}', 'delete'), '*51,62*') - - def test_addr_of_stacked(self): - src = ''' - #include - void alter(int *y) - { - *y += 5; - } - int main() - { - int x = 2; - alter(&x); - printf("*%d*\\n", x); - return 0; - } - ''' - self.do_run(src, '*7*') - - def test_globals(self): - src = ''' - #include - - char cache[256], *next = cache; - - int main() - { - cache[10] = 25; - next[20] = 51; - printf("*%d,%d*\\n", next[10], cache[20]); - return 0; - } - ''' - self.do_run(src, '*25,51*') - - def test_linked_list(self): - src = ''' - #include - struct worker_args { - int value; - struct worker_args *next; - }; - int main() - { - worker_args a; - worker_args b; - a.value = 60; - a.next = &b; - b.value = 900; - b.next = NULL; - worker_args* c = &a; - int total = 0; - while (c) { - total += c->value; - c = c->next; - } - - // Chunk of em - worker_args chunk[10]; - for (int i = 0; i < 9; i++) { - chunk[i].value = i*10; - chunk[i].next = &chunk[i+1]; - } - chunk[9].value = 90; - chunk[9].next = &chunk[0]; - - c = chunk; - do { - total += c->value; - c = c->next; - } while (c != chunk); - - printf("*%d,%d*\\n", total, b.next); - // NULL *is* 0, in C/C++. No JS null! (null == 0 is false, etc.) - - return 0; - } - ''' - self.do_run(src, '*1410,0*') - - def test_sup(self): - src = ''' - #include - - struct S4 { int x; }; // size: 4 - struct S4_2 { short x, y; }; // size: 4, but for alignment purposes, 2 - struct S6 { short x, y, z; }; // size: 6 - struct S6w { char x[6]; }; // size: 6 also - struct S6z { int x; short y; }; // size: 8, since we align to a multiple of the biggest - 4 - - struct C___ { S6 a, b, c; int later; }; - struct Carr { S6 a[3]; int later; }; // essentially the same, but differently defined - struct C__w { S6 a; S6w b; S6 c; int later; }; // same size, different struct - struct Cp1_ { int pre; short a; S6 b, c; int later; }; // fillers for a - struct Cp2_ { int a; short pre; S6 b, c; int later; }; // fillers for a (get addr of the other filler) - struct Cint { S6 a; int b; S6 c; int later; }; // An int (different size) for b - struct C4__ { S6 a; S4 b; S6 c; int later; }; // Same size as int from before, but a struct - struct C4_2 { S6 a; S4_2 b; S6 c; int later; }; // Same size as int from before, but a struct with max element size 2 - struct C__z { S6 a; S6z b; S6 c; int later; }; // different size, 8 instead of 6 - - int main() - { - #define TEST(struc) \\ - { \\ - struc *s = 0; \\ - printf("*%s: %d,%d,%d,%d<%d*\\n", #struc, (int)&(s->a), (int)&(s->b), (int)&(s->c), (int)&(s->later), sizeof(struc)); \\ - } - #define TEST_ARR(struc) \\ - { \\ - struc *s = 0; \\ - printf("*%s: %d,%d,%d,%d<%d*\\n", #struc, (int)&(s->a[0]), (int)&(s->a[1]), (int)&(s->a[2]), (int)&(s->later), sizeof(struc)); \\ - } - printf("sizeofs:%d,%d\\n", sizeof(S6), sizeof(S6z)); - TEST(C___); - TEST_ARR(Carr); - TEST(C__w); - TEST(Cp1_); - TEST(Cp2_); - TEST(Cint); - TEST(C4__); - TEST(C4_2); - TEST(C__z); - return 0; - } - ''' - if Settings.QUANTUM_SIZE == 1: - self.do_run(src, 'sizeofs:6,8\n*C___: 0,3,6,9<24*\n*Carr: 0,3,6,9<24*\n*C__w: 0,3,9,12<24*\n*Cp1_: 1,2,5,8<24*\n*Cp2_: 0,2,5,8<24*\n*Cint: 0,3,4,7<24*\n*C4__: 0,3,4,7<24*\n*C4_2: 0,3,5,8<20*\n*C__z: 0,3,5,8<28*') - else: - self.do_run(src, 'sizeofs:6,8\n*C___: 0,6,12,20<24*\n*Carr: 0,6,12,20<24*\n*C__w: 0,6,12,20<24*\n*Cp1_: 4,6,12,20<24*\n*Cp2_: 0,6,12,20<24*\n*Cint: 0,8,12,20<24*\n*C4__: 0,8,12,20<24*\n*C4_2: 0,6,10,16<20*\n*C__z: 0,8,16,24<28*') - - def test_assert(self): - src = ''' - #include - #include - int main() { - assert(1 == true); // pass - assert(1 == false); // fail - return 0; - } - ''' - self.do_run(src, 'Assertion failed: 1 == false') - - def test_libcextra(self): - if self.emcc_args is None: return self.skip('needs emcc for libcextra') - src = r''' - #include - #include - - int main() - { - const wchar_t* wstr = L"Hello"; - - printf("wcslen: %d\n", wcslen(wstr)); - - return 0; - } - ''' - self.do_run(src, 'wcslen: 5') - - def test_longjmp(self): - src = r''' - #include - #include - - static jmp_buf buf; - - void second(void) { - printf("second\n"); - longjmp(buf,-1); - } - - void first(void) { - printf("first\n"); // prints - longjmp(buf,1); // jumps back to where setjmp was called - making setjmp now return 1 - } - - int main() { - volatile int x = 0; - int jmpval = setjmp(buf); - if (!jmpval) { - x++; // should be properly restored once longjmp jumps back - first(); // when executed, setjmp returns 1 - printf("skipped\n"); // does not print - } else if (jmpval == 1) { // when first() jumps back, setjmp returns 1 - printf("result: %d %d\n", x, jmpval); // prints - x++; - second(); // when executed, setjmp returns -1 - } else if (jmpval == -1) { // when second() jumps back, setjmp returns -1 - printf("result: %d %d\n", x, jmpval); // prints - } - - return 0; - } - ''' - self.do_run(src, 'first\nresult: 1 1\nsecond\nresult: 2 -1') - - def test_longjmp2(self): - src = r''' - #include - #include - - typedef struct { - jmp_buf* jmp; - } jmp_state; - - void stack_manipulate_func(jmp_state* s, int level) { - jmp_buf buf; - - printf("Entering stack_manipulate_func, level: %d\n", level); - - if (level == 0) { - s->jmp = &buf; - if (setjmp(*(s->jmp)) == 0) { - printf("Setjmp normal execution path, level: %d\n", level); - stack_manipulate_func(s, level + 1); - } else { - printf("Setjmp error execution path, level: %d\n", level); - } - } else { - printf("Perform longjmp at level %d\n", level); - longjmp(*(s->jmp), 1); - } - - printf("Exiting stack_manipulate_func, level: %d\n", level); - } - - int main(int argc, char *argv[]) { - jmp_state s; - s.jmp = NULL; - stack_manipulate_func(&s, 0); - - return 0; - } - ''' - self.do_run(src, '''Entering stack_manipulate_func, level: 0 -Setjmp normal execution path, level: 0 -Entering stack_manipulate_func, level: 1 -Perform longjmp at level 1 -Setjmp error execution path, level: 0 -Exiting stack_manipulate_func, level: 0 -''') - - def test_longjmp3(self): - src = r''' - #include - #include - - typedef struct { - jmp_buf* jmp; - } jmp_state; - - void setjmp_func(jmp_state* s, int level) { - jmp_buf* prev_jmp = s->jmp; - jmp_buf c_jmp; - - if (level == 2) { - printf("level is 2, perform longjmp!\n"); - longjmp(*(s->jmp), 1); - } - - if (setjmp(c_jmp) == 0) { - printf("setjmp normal execution path, level: %d\n", level); - s->jmp = &c_jmp; - setjmp_func(s, level + 1); - } else { - printf("setjmp exception execution path, level: %d\n", level); - if (prev_jmp) { - printf("prev_jmp is not empty, continue with longjmp!\n"); - s->jmp = prev_jmp; - longjmp(*(s->jmp), 1); - } - } - - printf("Exiting setjmp function, level: %d\n", level); - } - - int main(int argc, char *argv[]) { - jmp_state s; - s.jmp = NULL; - - setjmp_func(&s, 0); - - return 0; - } - ''' - self.do_run(src, '''setjmp normal execution path, level: 0 -setjmp normal execution path, level: 1 -level is 2, perform longjmp! -setjmp exception execution path, level: 1 -prev_jmp is not empty, continue with longjmp! -setjmp exception execution path, level: 0 -Exiting setjmp function, level: 0 -''') - - def test_longjmp4(self): - src = r''' - #include - #include - - typedef struct { - jmp_buf* jmp; - } jmp_state; - - void second_func(jmp_state* s); - - void first_func(jmp_state* s) { - jmp_buf* prev_jmp = s->jmp; - jmp_buf c_jmp; - volatile int once = 0; - - if (setjmp(c_jmp) == 0) { - printf("Normal execution path of first function!\n"); - - s->jmp = &c_jmp; - second_func(s); - } else { - printf("Exception execution path of first function! %d\n", once); - - if (!once) { - printf("Calling longjmp the second time!\n"); - once = 1; - longjmp(*(s->jmp), 1); - } - } - } - - void second_func(jmp_state* s) { - longjmp(*(s->jmp), 1); - } - - int main(int argc, char *argv[]) { - jmp_state s; - s.jmp = NULL; - - first_func(&s); - - return 0; - } - ''' - self.do_run(src, '''Normal execution path of first function! -Exception execution path of first function! 0 -Calling longjmp the second time! -Exception execution path of first function! 1 -''') - - def test_longjmp_funcptr(self): - src = r''' - #include - #include - - static jmp_buf buf; - - void (*fp)() = NULL; - - void second(void) { - printf("second\n"); // prints - longjmp(buf,1); // jumps back to where setjmp was called - making setjmp now return 1 - } - - void first(void) { - fp(); - printf("first\n"); // does not print - } - - int main(int argc, char **argv) { - fp = argc == 200 ? NULL : second; - - volatile int x = 0; - if ( ! setjmp(buf) ) { - x++; - first(); // when executed, setjmp returns 0 - } else { // when longjmp jumps back, setjmp returns 1 - printf("main: %d\n", x); // prints - } - - return 0; - } - ''' - self.do_run(src, 'second\nmain: 1\n') - - def test_longjmp_repeat(self): - Settings.MAX_SETJMPS = 1 - - src = r''' - #include - #include - - static jmp_buf buf; - - int main() { - volatile int x = 0; - printf("setjmp:%d\n", setjmp(buf)); - x++; - printf("x:%d\n", x); - if (x < 4) longjmp(buf, x*2); - return 0; - } - ''' - self.do_run(src, '''setjmp:0 -x:1 -setjmp:2 -x:2 -setjmp:4 -x:3 -setjmp:6 -x:4 -''') - - def test_longjmp_stacked(self): - src = r''' - #include - #include - #include - #include - - int bottom, top; - - int run(int y) { - // confuse stack - char *s = (char*)alloca(100); - memset(s, 1, 100); - s[y] = y; - s[y/2] = y*2; - volatile int x = s[y]; - top = (int)alloca(4); - if (x <= 2) return x; - jmp_buf buf; - printf("setjmp of %d\n", x); - if (setjmp(buf) == 0) { - printf("going\n"); - x += run(x/2); - longjmp(buf, 1); - } - printf("back\n"); - return x/2; - } - - int main(int argc, char **argv) { - int sum = 0; - for (int i = 0; i < argc*2; i++) { - bottom = (int)alloca(4); - sum += run(10); - // scorch the earth - if (bottom < top) { - memset((void*)bottom, 1, top - bottom); - } else { - memset((void*)top, 1, bottom - top); - } - } - printf("%d\n", sum); - return sum; - } - ''' - self.do_run(src, '''setjmp of 10 -going -setjmp of 5 -going -back -back -setjmp of 10 -going -setjmp of 5 -going -back -back -12 -''') - - def test_longjmp_exc(self): - src = r''' - #include - #include - #include - #include - - jmp_buf abortframe; - - void dostuff(int a) { - printf("pre\n"); - if (a != 42) emscripten_run_script("waka_waka()"); // this should fail, and never reach "never" - printf("never\n"); - - if (a == 100) { - longjmp (abortframe, -1); - } - - if (setjmp(abortframe)) { - printf("got 100"); - } - } - - int main(int argc, char **argv) { - dostuff(argc); - exit(1); - return 1; - } - ''' - self.do_run(src, 'waka_waka'); - - def test_setjmp_many(self): - src = r''' - #include - #include - - int main(int argc) { - jmp_buf buf; - for (int i = 0; i < NUM; i++) printf("%d\n", setjmp(buf)); - if (argc-- == 1131) longjmp(buf, 11); - return 0; - } - ''' - for num in [Settings.MAX_SETJMPS, Settings.MAX_SETJMPS+1]: - print num - self.do_run(src.replace('NUM', str(num)), '0\n' * num if num <= Settings.MAX_SETJMPS or not Settings.ASM_JS else 'build with a higher value for MAX_SETJMPS') - - def test_exceptions(self): - if Settings.QUANTUM_SIZE == 1: return self.skip("we don't support libcxx in q1") - if self.emcc_args is None: return self.skip('need emcc to add in libcxx properly') - - Settings.EXCEPTION_DEBUG = 1 - - Settings.DISABLE_EXCEPTION_CATCHING = 0 - if '-O2' in self.emcc_args: - self.emcc_args += ['--closure', '1'] # Use closure here for some additional coverage - - src = ''' - #include - void thrower() { - printf("infunc..."); - throw(99); - printf("FAIL"); - } - int main() { - try { - printf("*throw..."); - throw(1); - printf("FAIL"); - } catch(...) { - printf("caught!"); - } - try { - thrower(); - } catch(...) { - printf("done!*\\n"); - } - return 0; - } - ''' - self.do_run(src, '*throw...caught!infunc...done!*') - - Settings.DISABLE_EXCEPTION_CATCHING = 1 - self.do_run(src, 'Exception catching is disabled, this exception cannot be caught. Compile with -s DISABLE_EXCEPTION_CATCHING=0') - - src = ''' - #include - - class MyException - { - public: - MyException(){ std::cout << "Construct..."; } - MyException( const MyException & ) { std::cout << "Copy..."; } - ~MyException(){ std::cout << "Destruct..."; } - }; - - int function() - { - std::cout << "Throw..."; - throw MyException(); - } - - int function2() - { - return function(); - } - - int main() - { - try - { - function2(); - } - catch (MyException & e) - { - std::cout << "Catched..."; - } - - try - { - function2(); - } - catch (MyException e) - { - std::cout << "Catched..."; - } - - return 0; - } - ''' - - Settings.DISABLE_EXCEPTION_CATCHING = 0 - if '-O2' in self.emcc_args: - self.emcc_args.pop() ; self.emcc_args.pop() # disable closure to work around a closure bug - self.do_run(src, 'Throw...Construct...Catched...Destruct...Throw...Construct...Copy...Catched...Destruct...Destruct...') - - def test_exception_2(self): - if self.emcc_args is None: return self.skip('need emcc to add in libcxx properly') - Settings.DISABLE_EXCEPTION_CATCHING = 0 - src = r''' - #include - #include - - typedef void (*FuncPtr)(); - - void ThrowException() - { - throw std::runtime_error("catch me!"); - } - - FuncPtr ptr = ThrowException; - - int main() - { - try - { - ptr(); - } - catch(...) - { - printf("Exception caught successfully!\n"); - } - return 0; - } - ''' - self.do_run(src, 'Exception caught successfully!') - - def test_white_list_exception(self): - Settings.DISABLE_EXCEPTION_CATCHING = 2 - Settings.EXCEPTION_CATCHING_WHITELIST = ["__Z12somefunctionv"] - Settings.INLINING_LIMIT = 50 # otherwise it is inlined and not identified - - src = ''' - #include - - void thrower() { - printf("infunc..."); - throw(99); - printf("FAIL"); - } - - void somefunction() { - try { - thrower(); - } catch(...) { - printf("done!*\\n"); - } - } - - int main() { - somefunction(); - return 0; - } - ''' - self.do_run(src, 'infunc...done!*') - - Settings.DISABLE_EXCEPTION_CATCHING = 0 - Settings.EXCEPTION_CATCHING_WHITELIST = [] - - def test_uncaught_exception(self): - if self.emcc_args is None: return self.skip('no libcxx inclusion without emcc') - - Settings.DISABLE_EXCEPTION_CATCHING = 0 - - src = r''' - #include - #include - struct X { - ~X() { - printf("exception? %s\n", std::uncaught_exception() ? "yes" : "no"); - } - }; - int main() { - printf("exception? %s\n", std::uncaught_exception() ? "yes" : "no"); - try { - X x; - throw 1; - } catch(...) { - printf("exception? %s\n", std::uncaught_exception() ? "yes" : "no"); - } - printf("exception? %s\n", std::uncaught_exception() ? "yes" : "no"); - return 0; - } - ''' - self.do_run(src, 'exception? no\nexception? yes\nexception? no\nexception? no\n') - - src = r''' - #include - #include - int main() { - std::ofstream os("test"); - os << std::unitbuf << "foo"; // trigger a call to std::uncaught_exception from - // std::basic_ostream::sentry::~sentry - std::cout << "success"; - } - ''' - self.do_run(src, 'success') - - def test_typed_exceptions(self): - Settings.DISABLE_EXCEPTION_CATCHING = 0 - Settings.SAFE_HEAP = 0 # Throwing null will cause an ignorable null pointer access. - src = open(path_from_root('tests', 'exceptions', 'typed.cpp'), 'r').read() - expected = open(path_from_root('tests', 'exceptions', 'output.txt'), 'r').read() - self.do_run(src, expected) - - def test_multiexception(self): - Settings.DISABLE_EXCEPTION_CATCHING = 0 - src = r''' -#include - -static int current_exception_id = 0; - -typedef struct { - int jmp; -} jmp_state; - -void setjmp_func(jmp_state* s, int level) { - int prev_jmp = s->jmp; - int c_jmp; - - if (level == 2) { - printf("level is 2, perform longjmp!\n"); - throw 1; - } - - c_jmp = current_exception_id++; - try { - printf("setjmp normal execution path, level: %d, prev_jmp: %d\n", level, prev_jmp); - s->jmp = c_jmp; - setjmp_func(s, level + 1); - } catch (int catched_eid) { - printf("caught %d\n", catched_eid); - if (catched_eid == c_jmp) { - printf("setjmp exception execution path, level: %d, prev_jmp: %d\n", level, prev_jmp); - if (prev_jmp != -1) { - printf("prev_jmp is not empty, continue with longjmp!\n"); - s->jmp = prev_jmp; - throw s->jmp; - } - } else { - throw; - } - } - - printf("Exiting setjmp function, level: %d, prev_jmp: %d\n", level, prev_jmp); -} - -int main(int argc, char *argv[]) { - jmp_state s; - s.jmp = -1; - - setjmp_func(&s, 0); - - return 0; -} -''' - self.do_run(src, '''setjmp normal execution path, level: 0, prev_jmp: -1 -setjmp normal execution path, level: 1, prev_jmp: 0 -level is 2, perform longjmp! -caught 1 -setjmp exception execution path, level: 1, prev_jmp: 0 -prev_jmp is not empty, continue with longjmp! -caught 0 -setjmp exception execution path, level: 0, prev_jmp: -1 -Exiting setjmp function, level: 0, prev_jmp: -1 -''') - - def test_std_exception(self): - if self.emcc_args is None: return self.skip('requires emcc') - Settings.DISABLE_EXCEPTION_CATCHING = 0 - self.emcc_args += ['-s', 'SAFE_HEAP=0'] - - src = r''' - #include - #include - - int main() - { - std::exception e; - try { - throw e; - } catch(std::exception e) { - printf("caught std::exception\n"); - } - return 0; - } - ''' - self.do_run(src, 'caught std::exception') - - def test_async_exit(self): - open('main.c', 'w').write(r''' - #include - #include - #include "emscripten.h" - - void main_loop() { - exit(EXIT_SUCCESS); - } - - int main() { - emscripten_set_main_loop(main_loop, 60, 0); - return 0; - } - ''') - - Popen([PYTHON, EMCC, 'main.c']).communicate() - self.assertNotContained('Reached an unreachable!', run_js(self.in_dir('a.out.js'), stderr=STDOUT)) - - def test_exit_stack(self): - if self.emcc_args is None: return self.skip('requires emcc') - if Settings.ASM_JS: return self.skip('uses report_stack without exporting') - - Settings.INLINING_LIMIT = 50 - - src = r''' - #include - #include - - extern "C" { - extern void report_stack(int x); - } - - char moar() { - char temp[125]; - for (int i = 0; i < 125; i++) temp[i] = i*i; - for (int i = 1; i < 125; i++) temp[i] += temp[i-1]/2; - if (temp[100] != 99) exit(1); - return temp[120]; - } - - int main(int argc, char *argv[]) { - report_stack((int)alloca(4)); - printf("*%d*\n", moar()); - return 0; - } - ''' - - open(os.path.join(self.get_dir(), 'pre.js'), 'w').write(''' - var initialStack = -1; - var _report_stack = function(x) { - Module.print('reported'); - initialStack = x; - } - var Module = { - postRun: function() { - Module.print('Exit Status: ' + EXITSTATUS); - Module.print('postRun'); - assert(initialStack == STACKTOP, [initialStack, STACKTOP]); - Module.print('ok.'); - } - }; - ''') - - self.emcc_args += ['--pre-js', 'pre.js'] - self.do_run(src, '''reported\nexit(1) called\nExit Status: 1\npostRun\nok.\n''') - - def test_class(self): - src = ''' - #include - struct Random { - enum { IM = 139968, IA = 3877, IC = 29573 }; - Random() : last(42) {} - float get( float max = 1.0f ) { - last = ( last * IA + IC ) % IM; - return max * last / IM; - } - protected: - unsigned int last; - } rng1; - int main() - { - Random rng2; - int count = 0; - for (int i = 0; i < 100; i++) { - float x1 = rng1.get(); - float x2 = rng2.get(); - printf("%f, %f\\n", x1, x2); - if (x1 != x2) count += 1; - } - printf("*%d*\\n", count); - return 0; - } - ''' - self.do_run(src, '*0*') - - def test_inherit(self): - src = ''' - #include - struct Parent { - int x1, x2; - }; - struct Child : Parent { - int y; - }; - int main() - { - Parent a; - a.x1 = 50; - a.x2 = 87; - Child b; - b.x1 = 78; - b.x2 = 550; - b.y = 101; - Child* c = (Child*)&a; - c->x1 ++; - c = &b; - c->y --; - printf("*%d,%d,%d,%d,%d,%d,%d*\\n", a.x1, a.x2, b.x1, b.x2, b.y, c->x1, c->x2); - return 0; - } - ''' - self.do_run(src, '*51,87,78,550,100,78,550*') - - def test_isdigit_l(self): - if self.emcc_args is None: return self.skip('no libcxx inclusion without emcc') - - src = ''' - #include - int main() { - using namespace std; - use_facet >(cout.getloc()).put(cout, cout, '0', 3.14159265); - } - ''' - self.do_run(src, '3.14159') - - def test_polymorph(self): - if self.emcc_args is None: return self.skip('requires emcc') - src = ''' - #include - struct Pure { - virtual int implme() = 0; - }; - struct Parent : Pure { - virtual int getit() { return 11; }; - int implme() { return 32; } - }; - struct Child : Parent { - int getit() { return 74; } - int implme() { return 1012; } - }; - - struct Other { - int one() { return 11; } - int two() { return 22; } - }; - - int main() - { - Parent *x = new Parent(); - Parent *y = new Child(); - printf("*%d,%d,%d,%d*\\n", x->getit(), y->getit(), x->implme(), y->implme()); - - Other *o = new Other; - int (Other::*Ls)() = &Other::one; - printf("*%d*\\n", (o->*(Ls))()); - Ls = &Other::two; - printf("*%d*\\n", (o->*(Ls))()); - - return 0; - } - ''' - self.do_run(src, '*11,74,32,1012*\n*11*\n*22*') - - def test_segfault(self): - if self.emcc_args is None: return self.skip('SAFE_HEAP without ta2 means we check types too, which hide segfaults') - if Settings.ASM_JS: return self.skip('asm does not support safe heap') - - Settings.SAFE_HEAP = 1 - - for addr in ['0', 'new D2()']: - print addr - src = r''' - #include - - struct Classey { - virtual void doIt() = 0; - }; - - struct D1 : Classey { - virtual void doIt() { printf("fleefl\n"); } - }; - - struct D2 : Classey { - virtual void doIt() { printf("marfoosh\n"); } - }; - - int main(int argc, char **argv) - { - Classey *p = argc == 100 ? new D1() : (Classey*)%s; - - p->doIt(); - - return 0; - } - ''' % addr - self.do_run(src, 'segmentation fault' if addr.isdigit() else 'marfoosh') - - def test_safe_dyncalls(self): - if Settings.ASM_JS: return self.skip('asm does not support missing function stack traces') - if Settings.SAFE_HEAP: return self.skip('safe heap warning will appear instead') - if self.emcc_args is None: return self.skip('need libc') - - Settings.SAFE_DYNCALLS = 1 - - for cond, body, work in [(True, True, False), (True, False, False), (False, True, True), (False, False, False)]: - print cond, body, work - src = r''' - #include - - struct Classey { - virtual void doIt() = 0; - }; - - struct D1 : Classey { - virtual void doIt() BODY; - }; - - int main(int argc, char **argv) - { - Classey *p = argc COND 100 ? new D1() : NULL; - printf("%p\n", p); - p->doIt(); - - return 0; - } - '''.replace('COND', '==' if cond else '!=').replace('BODY', r'{ printf("all good\n"); }' if body else '') - self.do_run(src, 'dyncall error: vi' if not work else 'all good') - - def test_dynamic_cast(self): - if self.emcc_args is None: return self.skip('need libcxxabi') - - src = r''' - #include - - struct Support { - virtual void f() { - printf("f()\n"); - } - }; - - struct Derived : Support { - }; - - int main() { - Support * p = new Derived; - dynamic_cast(p)->f(); - } - ''' - self.do_run(src, 'f()\n') - - def test_dynamic_cast_b(self): - if self.emcc_args is None: return self.skip('need libcxxabi') - - src = ''' - #include - - class CBase { virtual void dummy() {} }; - class CDerived : public CBase { int a; }; - class CDerivedest : public CDerived { float b; }; - - int main () - { - CBase *pa = new CBase; - CBase *pb = new CDerived; - CBase *pc = new CDerivedest; - - printf("a1: %d\\n", dynamic_cast(pa) != NULL); - printf("a2: %d\\n", dynamic_cast(pa) != NULL); - printf("a3: %d\\n", dynamic_cast(pa) != NULL); - - printf("b1: %d\\n", dynamic_cast(pb) != NULL); - printf("b2: %d\\n", dynamic_cast(pb) != NULL); - printf("b3: %d\\n", dynamic_cast(pb) != NULL); - - printf("c1: %d\\n", dynamic_cast(pc) != NULL); - printf("c2: %d\\n", dynamic_cast(pc) != NULL); - printf("c3: %d\\n", dynamic_cast(pc) != NULL); - - return 0; - } - ''' - self.do_run(src, 'a1: 0\na2: 0\na3: 1\nb1: 0\nb2: 1\nb3: 1\nc1: 1\nc2: 1\nc3: 1\n') - - def test_dynamic_cast_2(self): - if self.emcc_args is None: return self.skip('need libcxxabi') - - src = r''' - #include - #include - - class Class {}; - - int main() { - const Class* dp = dynamic_cast(&typeid(Class)); - // should return dp == NULL, - printf("pointer: %p\n", dp); - } - ''' - self.do_run(src, "pointer: (nil)") - - def test_funcptr(self): - src = ''' - #include - int calc1() { return 26; } - int calc2() { return 90; } - typedef int (*fp_t)(); - - fp_t globally1 = calc1; - fp_t globally2 = calc2; - - int nothing(const char *str) { return 0; } - - int main() - { - fp_t fp = calc1; - void *vp = (void*)fp; - fp_t fpb = (fp_t)vp; - fp_t fp2 = calc2; - void *vp2 = (void*)fp2; - fp_t fpb2 = (fp_t)vp2; - printf("*%d,%d,%d,%d,%d,%d*\\n", fp(), fpb(), fp2(), fpb2(), globally1(), globally2()); - - fp_t t = calc1; - printf("*%d,%d", t == calc1, t == calc2); - t = calc2; - printf(",%d,%d*\\n", t == calc1, t == calc2); - - int (*other)(const char *str); - other = nothing; - other("*hello!*"); - other = puts; - other("*goodbye!*"); - - return 0; - } - ''' - self.do_run(src, '*26,26,90,90,26,90*\n*1,0,0,1*\n*goodbye!*') - - def test_mathfuncptr(self): - src = ''' - #include - #include - - int - main(int argc, char **argv) { - float (*fn)(float) = argc != 12 ? &sqrtf : &fabsf; - float (*fn2)(float) = argc != 13 ? &fabsf : &sqrtf; - float (*fn3)(float) = argc != 14 ? &erff : &fabsf; - printf("fn2(-5) = %d, fn(10) = %.2f, erf(10) = %.2f\\n", (int)fn2(-5), fn(10), fn3(10)); - return 0; - } - ''' - self.do_run(src, 'fn2(-5) = 5, fn(10) = 3.16, erf(10) = 1.00') - - def test_funcptrfunc(self): - src = r''' - #include - - typedef void (*funcptr)(int, int); - typedef funcptr (*funcptrfunc)(int); - - funcptr __attribute__ ((noinline)) getIt(int x) { - return (funcptr)x; - } - - int main(int argc, char **argv) - { - funcptrfunc fpf = argc < 100 ? getIt : NULL; - printf("*%p*\n", fpf(argc)); - return 0; - } - ''' - self.do_run(src, '*0x1*') - - def test_funcptr_namecollide(self): - src = r''' - #include - - void do_call(void (*puts)(const char *), const char *str); - - void do_print(const char *str) { - if (!str) do_call(NULL, "delusion"); - if ((int)str == -1) do_print(str+10); - puts("===="); - puts(str); - puts("===="); - } - - void do_call(void (*puts)(const char *), const char *str) { - if (!str) do_print("confusion"); - if ((int)str == -1) do_call(NULL, str-10); - (*puts)(str); - } - - int main(int argc, char **argv) - { - for (int i = 0; i < argc; i++) { - do_call(i != 10 ? do_print : NULL, i != 15 ? "waka waka" : NULL); - } - return 0; - } - ''' - self.do_run(src, 'waka', force_c=True) - - def test_emptyclass(self): - if self.emcc_args is None: return self.skip('requires emcc') - src = ''' - #include - - struct Randomized { - Randomized(int x) { - printf("*zzcheezzz*\\n"); - } - }; - - int main( int argc, const char *argv[] ) { - new Randomized(55); - - return 0; - } - ''' - self.do_run(src, '*zzcheezzz*') - - def test_alloca(self): - src = ''' - #include - #include - - int main() { - char *pc; - pc = (char *)alloca(5); - printf("z:%d*%d*\\n", pc > 0, (int)pc); - return 0; - } - ''' - self.do_run(src, 'z:1*', force_c=True) - - def test_rename(self): - src = open(path_from_root('tests', 'stdio', 'test_rename.c'), 'r').read() - self.do_run(src, 'success', force_c=True) - - def test_alloca_stack(self): - if self.emcc_args is None: return # too slow in other modes - - # We should not blow up the stack with numerous allocas - src = ''' - #include - #include - - func(int i) { - char *pc = (char *)alloca(100); - *pc = i; - (*pc)++; - return (*pc) % 10; - } - int main() { - int total = 0; - for (int i = 0; i < 1024*1024; i++) - total += func(i); - printf("ok:%d*\\n", total); - return 0; - } - ''' - self.do_run(src, 'ok:-32768*', force_c=True) - - def test_stack_byval(self): - if self.emcc_args is None: return # too slow in other modes - - # We should also not blow up the stack with byval arguments - src = r''' - #include - struct vec { - int x, y, z; - vec(int x_, int y_, int z_) : x(x_), y(y_), z(z_) {} - static vec add(vec a, vec b) { - return vec(a.x+b.x, a.y+b.y, a.z+b.z); - } - }; - int main() { - int total = 0; - for (int i = 0; i < 1000; i++) { - for (int j = 0; j < 1000; j++) { - vec c(i+i%10, j*2, i%255); - vec d(j*2, j%255, i%120); - vec f = vec::add(c, d); - total += (f.x + f.y + f.z) % 100; - total %= 10240; - } - } - printf("sum:%d*\n", total); - return 0; - } - ''' - self.do_run(src, 'sum:9780*') - - def test_stack_varargs(self): - if self.emcc_args is None: return # too slow in other modes - - Settings.INLINING_LIMIT = 50 - - # We should not blow up the stack with numerous varargs - src = r''' - #include - #include - - 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_stack_varargs2(self): - if self.emcc_args is None: return # too slow in other modes - Settings.TOTAL_STACK = 1024 - src = r''' - #include - #include - - void func(int i) { - } - int main() { - for (int i = 0; i < 1024; 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); - } - printf("ok!\n"); - return 0; - } - ''' - self.do_run(src, 'ok!') - - print 'with return' - - src = r''' - #include - #include - - int main() { - for (int i = 0; i < 1024; i++) { - int j = 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", - 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); - printf(" (%d)\n", j); - } - printf("ok!\n"); - return 0; - } - ''' - self.do_run(src, 'ok!') - - print 'with definitely no return' - - src = r''' - #include - #include - #include - - void vary(const char *s, ...) - { - va_list v; - va_start(v, s); - char d[20]; - vsnprintf(d, 20, s, v); - puts(d); - - // Try it with copying - va_list tempva; - va_copy(tempva, v); - vsnprintf(d, 20, s, tempva); - puts(d); - - va_end(v); - } - - int main() { - for (int i = 0; i < 1024; i++) { - int j = 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", - 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); - printf(" (%d)\n", j); - vary("*cheez: %d+%d*", 99, 24); - vary("*albeit*"); - } - printf("ok!\n"); - return 0; - } - ''' - self.do_run(src, 'ok!') - - def test_stack_void(self): - Settings.INLINING_LIMIT = 50 - - src = r''' - #include - - static char s[100]="aaaaa"; - static int func(void) { - if(s[0]!='a') return 0; - printf("iso open %s\n", s, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001); - return 0; - } - int main(){ - int i; - for(i=0;i<5000;i++) - func(); - printf(".ok.\n"); - } - ''' - self.do_run(src, '.ok.\n') - - def test_life(self): - if self.emcc_args is None: return self.skip('need c99') - self.emcc_args += ['-std=c99'] - src = open(path_from_root('tests', 'life.c'), 'r').read() - self.do_run(src, '''-------------------------------- -[] [] [][][] - [] [] [] [][] [] [] [] -[] [][] [][] [][][] [] - [] [] [] [] [][] [] [] - [] [][] [] [] [] [] [][][][] - [][] [][] [] [][][] [] [] - [] [][] [][] [][] [][][] - [][] [][][] [] [] - [][] [][] [] - [][][] - [] - - - - - [][][] - [] [][] [][] - [][] [] [][] [][] - [][] [][] - [] - [][] - [][] [] -[] [][] [] - [][][] [] - [] [][] -[] [] [] - [] -[] [] [] - [][][] - - [] - [][][] [] --------------------------------- -''', ['2'], force_c=True) - - def test_array2(self): - src = ''' - #include - - static const double grid[4][2] = { - {-3/3.,-1/3.},{+1/3.,-3/3.}, - {-1/3.,+3/3.},{+3/3.,+1/3.} - }; - - int main() { - for (int i = 0; i < 4; i++) - printf("%d:%.2f,%.2f ", i, grid[i][0], grid[i][1]); - printf("\\n"); - return 0; - } - ''' - self.do_run(src, '0:-1.00,-0.33 1:0.33,-1.00 2:-0.33,1.00 3:1.00,0.33') - - def test_array2b(self): - src = ''' - #include - - static const struct { - unsigned char left; - unsigned char right; - } prioritah[] = { - {6, 6}, {6, 6}, {7, 95}, {7, 7} - }; - - int main() { - printf("*%d,%d\\n", prioritah[1].left, prioritah[1].right); - printf("%d,%d*\\n", prioritah[2].left, prioritah[2].right); - return 0; - } - ''' - self.do_run(src, '*6,6\n7,95*') - - - def test_constglobalstructs(self): - src = ''' - #include - struct IUB { - int c; - double p; - unsigned int pi; - }; - - IUB iub[] = { - { 'a', 0.27, 5 }, - { 'c', 0.15, 4 }, - { 'g', 0.12, 3 }, - { 't', 0.27, 2 }, - }; - - const unsigned char faceedgesidx[6][4] = - { - { 4, 5, 8, 10 }, - { 6, 7, 9, 11 }, - { 0, 2, 8, 9 }, - { 1, 3, 10,11 }, - { 0, 1, 4, 6 }, - { 2, 3, 5, 7 }, - }; - - int main( int argc, const char *argv[] ) { - printf("*%d,%d,%d,%d*\\n", iub[0].c, int(iub[1].p*100), iub[2].pi, faceedgesidx[3][2]); - return 0; - } - ''' - self.do_run(src, '*97,15,3,10*') - - def test_conststructs(self): - src = ''' - #include - struct IUB { - int c; - double p; - unsigned int pi; - }; - - int main( int argc, const char *argv[] ) { - int before = 70; - IUB iub[] = { - { 'a', 0.3029549426680, 5 }, - { 'c', 0.15, 4 }, - { 'g', 0.12, 3 }, - { 't', 0.27, 2 }, - }; - int after = 90; - printf("*%d,%d,%d,%d,%d,%d*\\n", before, iub[0].c, int(iub[1].p*100), iub[2].pi, int(iub[0].p*10000), after); - return 0; - } - ''' - self.do_run(src, '*70,97,15,3,3029,90*') - - def test_bigarray(self): - if self.emcc_args is None: return self.skip('need ta2 to compress type data on zeroinitializers') - - # avoid "array initializer too large" errors - src = r''' - #include - #include - - #define SIZE (1024*100) - struct Struct { - char x; - int y; - }; - Struct buffy[SIZE]; - - int main() { - for (int i = 0; i < SIZE; i++) { assert(buffy[i].x == 0 && buffy[i].y == 0); } // we were zeroinitialized - for (int i = 0; i < SIZE; i++) { buffy[i].x = i*i; buffy[i].y = i*i*i; } // we can save data - printf("*%d*\n", buffy[SIZE/3].x); - return 0; - } - ''' - self.do_run(src, '*57*') - - def test_mod_globalstruct(self): - src = ''' - #include - - struct malloc_params { - size_t magic, page_size; - }; - - malloc_params mparams; - - #define SIZE_T_ONE ((size_t)1) - #define page_align(S) (((S) + (mparams.page_size - SIZE_T_ONE)) & ~(mparams.page_size - SIZE_T_ONE)) - - int main() - { - mparams.page_size = 4096; - printf("*%d,%d,%d,%d*\\n", mparams.page_size, page_align(1000), page_align(6000), page_align(66474)); - return 0; - } - ''' - self.do_run(src, '*4096,4096,8192,69632*') - - def test_pystruct(self): - src = ''' - #include - - // Based on CPython code - union PyGC_Head { - struct { - union PyGC_Head *gc_next; - union PyGC_Head *gc_prev; - size_t gc_refs; - } gc; - long double dummy; /* force worst-case alignment */ - } ; - - struct gc_generation { - PyGC_Head head; - int threshold; /* collection threshold */ - int count; /* count of allocations or collections of younger - generations */ - }; - - #define NUM_GENERATIONS 3 - #define GEN_HEAD(n) (&generations[n].head) - - /* linked lists of container objects */ - static struct gc_generation generations[NUM_GENERATIONS] = { - /* PyGC_Head, threshold, count */ - {{{GEN_HEAD(0), GEN_HEAD(0), 0}}, 700, 0}, - {{{GEN_HEAD(1), GEN_HEAD(1), 0}}, 10, 0}, - {{{GEN_HEAD(2), GEN_HEAD(2), 0}}, 10, 0}, - }; - - int main() - { - gc_generation *n = NULL; - printf("*%d,%d,%d,%d,%d,%d,%d,%d*\\n", - (int)(&n[0]), - (int)(&n[0].head), - (int)(&n[0].head.gc.gc_next), - (int)(&n[0].head.gc.gc_prev), - (int)(&n[0].head.gc.gc_refs), - (int)(&n[0].threshold), (int)(&n[0].count), (int)(&n[1]) - ); - printf("*%d,%d,%d*\\n", - (int)(&generations[0]) == - (int)(&generations[0].head.gc.gc_next), - (int)(&generations[0]) == - (int)(&generations[0].head.gc.gc_prev), - (int)(&generations[0]) == - (int)(&generations[1]) - ); - int x1 = (int)(&generations[0]); - int x2 = (int)(&generations[1]); - printf("*%d*\\n", x1 == x2); - for (int i = 0; i < NUM_GENERATIONS; i++) { - PyGC_Head *list = GEN_HEAD(i); - printf("%d:%d,%d\\n", i, (int)list == (int)(list->gc.gc_prev), (int)list ==(int)(list->gc.gc_next)); - } - printf("*%d,%d,%d*\\n", sizeof(PyGC_Head), sizeof(gc_generation), int(GEN_HEAD(2)) - int(GEN_HEAD(1))); - } - ''' - if Settings.QUANTUM_SIZE == 1: - # Compressed memory. Note that sizeof() does give the fat sizes, however! - self.do_run(src, '*0,0,0,1,2,3,4,5*\n*1,0,0*\n*0*\n0:1,1\n1:1,1\n2:1,1\n*12,20,5*') - else: - if self.is_le32(): - self.do_run(src, '*0,0,0,4,8,16,20,24*\n*1,0,0*\n*0*\n0:1,1\n1:1,1\n2:1,1\n*16,24,24*') - else: - self.do_run(src, '*0,0,0,4,8,12,16,20*\n*1,0,0*\n*0*\n0:1,1\n1:1,1\n2:1,1\n*12,20,20*') - - def test_ptrtoint(self): - if self.emcc_args is None: return self.skip('requires emcc') - src = ''' - #include - - int main( int argc, const char *argv[] ) { - char *a = new char[10]; - char *a0 = a+0; - char *a5 = a+5; - int *b = new int[10]; - int *b0 = b+0; - int *b5 = b+5; - int c = (int)b5-(int)b0; // Emscripten should warn! - int d = (int)b5-(int)b0; // Emscripten should warn! - printf("*%d*\\n", (int)a5-(int)a0); - return 0; - } - ''' - runner = self - def check_warnings(output): - runner.assertEquals(filter(lambda line: 'Warning' in line, output.split('\n')).__len__(), 4) - self.do_run(src, '*5*', output_processor=check_warnings) - - def test_sizeof(self): - if self.emcc_args is None: return self.skip('requires emcc') - # Has invalid writes between printouts - Settings.SAFE_HEAP = 0 - - src = ''' - #include - #include - #include "emscripten.h" - - struct A { int x, y; }; - - int main( int argc, const char *argv[] ) { - int *a = new int[10]; - int *b = new int[1]; - int *c = new int[10]; - for (int i = 0; i < 10; i++) - a[i] = 2; - *b = 5; - for (int i = 0; i < 10; i++) - c[i] = 8; - printf("*%d,%d,%d,%d,%d*\\n", a[0], a[9], *b, c[0], c[9]); - // Should overwrite a, but not touch b! - memcpy(a, c, 10*sizeof(int)); - printf("*%d,%d,%d,%d,%d*\\n", a[0], a[9], *b, c[0], c[9]); - - // Part 2 - A as[3] = { { 5, 12 }, { 6, 990 }, { 7, 2 } }; - memcpy(&as[0], &as[2], sizeof(A)); - - printf("*%d,%d,%d,%d,%d,%d*\\n", as[0].x, as[0].y, as[1].x, as[1].y, as[2].x, as[2].y); - return 0; - } - ''' - self.do_run(src, '*2,2,5,8,8***8,8,5,8,8***7,2,6,990,7,2*', [], lambda x, err: x.replace('\n', '*')) - - def test_float_h(self): - process = Popen([PYTHON, EMCC, path_from_root('tests', 'float+.c')], stdout=PIPE, stderr=PIPE) - process.communicate() - assert process.returncode is 0, 'float.h should agree with our system' - - def test_emscripten_api(self): - #if Settings.MICRO_OPTS or Settings.RELOOP or Building.LLVM_OPTS: return self.skip('FIXME') - - src = r''' - #include - #include "emscripten.h" - - extern "C" { - void save_me_aimee() { printf("mann\n"); } - } - - int main() { - // EMSCRIPTEN_COMMENT("hello from the source"); - emscripten_run_script("Module.print('hello world' + '!')"); - printf("*%d*\n", emscripten_run_script_int("5*20")); - printf("*%s*\n", emscripten_run_script_string("'five'+'six'")); - emscripten_run_script("Module['_save_me_aimee']()"); - return 0; - } - ''' - - check = ''' -def process(filename): - src = open(filename, 'r').read() - # TODO: restore this (see comment in emscripten.h) assert '// hello from the source' in src -''' - Settings.EXPORTED_FUNCTIONS = ['_main', '_save_me_aimee'] - self.do_run(src, 'hello world!\n*100*\n*fivesix*\nmann\n', post_build=check) - - # test EXPORT_ALL - Settings.EXPORTED_FUNCTIONS = [] - Settings.EXPORT_ALL = 1 - self.do_run(src, 'hello world!\n*100*\n*fivesix*\nmann\n', post_build=check) - - def test_inlinejs(self): - if Settings.ASM_JS: return self.skip('asm does not support random code, TODO: something that works in asm') - src = r''' - #include - - double get() { - double ret = 0; - __asm __volatile__("Math.abs(-12/3.3)":"=r"(ret)); // write to a variable - return ret; - } - - int main() { - asm("Module.print('Inline JS is very cool')"); - printf("%.2f\n", get()); - return 0; - } - ''' - - self.do_run(src, 'Inline JS is very cool\n3.64') - - def test_inlinejs2(self): - if Settings.ASM_JS: return self.skip('asm does not support random code, TODO: something that works in asm') - src = r''' - #include - - int mix(int x, int y) { - int ret; - asm("Math.pow(2, %0+%1+1)" : "=r"(ret) : "r"(x), "r"(y)); // read and write - return ret; - } - - void mult() { - asm("var $_$1 = Math.abs(-100); $_$1 *= 2;"); // multiline - asm __volatile__("Module.print($_$1); Module.print('\n')"); - } - - int main(int argc, char **argv) { - printf("%d\n", mix(argc, argc/2)); - mult(); - return 0; - } - ''' - - self.do_run(src, '4\n200\n') - - def test_memorygrowth(self): - if Settings.USE_TYPED_ARRAYS == 0: return self.skip('memory growth is only supported with typed arrays') - if Settings.ASM_JS: return self.skip('asm does not support memory growth yet') - - # With typed arrays in particular, it is dangerous to use more memory than TOTAL_MEMORY, - # since we then need to enlarge the heap(s). - src = r''' - #include - #include - #include - #include - #include "emscripten.h" - - int main(int argc, char **argv) - { - char *buf1 = (char*)malloc(100); - char *data1 = "hello"; - memcpy(buf1, data1, strlen(data1)+1); - - float *buf2 = (float*)malloc(100); - float pie = 4.955; - memcpy(buf2, &pie, sizeof(float)); - - printf("*pre: %s,%.3f*\n", buf1, buf2[0]); - - int totalMemory = emscripten_run_script_int("TOTAL_MEMORY"); - char *buf3 = (char*)malloc(totalMemory+1); - buf3[argc] = (int)buf2; - if (argc % 7 == 6) printf("%d\n", memcpy(buf3, buf1, argc)); - char *buf4 = (char*)malloc(100); - float *buf5 = (float*)malloc(100); - //printf("totalMemory: %d bufs: %d,%d,%d,%d,%d\n", totalMemory, buf1, buf2, buf3, buf4, buf5); - assert((int)buf4 > (int)totalMemory && (int)buf5 > (int)totalMemory); - - printf("*%s,%.3f*\n", buf1, buf2[0]); // the old heap data should still be there - - memcpy(buf4, buf1, strlen(data1)+1); - memcpy(buf5, buf2, sizeof(float)); - printf("*%s,%.3f*\n", buf4, buf5[0]); // and the new heap space should work too - - return 0; - } - ''' - - # Fail without memory growth - self.do_run(src, 'Cannot enlarge memory arrays.') - fail = open('src.cpp.o.js').read() - - # Win with it - Settings.ALLOW_MEMORY_GROWTH = 1 - self.do_run(src, '*pre: hello,4.955*\n*hello,4.955*\n*hello,4.955*') - win = open('src.cpp.o.js').read() - - if self.emcc_args and '-O2' in self.emcc_args: - # Make sure ALLOW_MEMORY_GROWTH generates different code (should be less optimized) - code_start = 'var TOTAL_MEMORY = ' - fail = fail[fail.find(code_start):] - win = win[win.find(code_start):] - assert len(fail) < len(win), 'failing code - without memory growth on - is more optimized, and smaller' - - def test_ssr(self): # struct self-ref - src = ''' - #include - - // see related things in openjpeg - typedef struct opj_mqc_state { - unsigned int qeval; - int mps; - struct opj_mqc_state *nmps; - struct opj_mqc_state *nlps; - } opj_mqc_state_t; - - static opj_mqc_state_t mqc_states[2] = { - {0x5600, 0, &mqc_states[2], &mqc_states[3]}, - {0x5602, 1, &mqc_states[3], &mqc_states[2]}, - }; - - int main() { - printf("*%d*\\n", (int)(mqc_states+1)-(int)mqc_states); - for (int i = 0; i < 2; i++) - printf("%d:%d,%d,%d,%d\\n", i, mqc_states[i].qeval, mqc_states[i].mps, - (int)mqc_states[i].nmps-(int)mqc_states, (int)mqc_states[i].nlps-(int)mqc_states); - return 0; - } - ''' - if Settings.QUANTUM_SIZE == 1: - self.do_run(src, '''*4*\n0:22016,0,8,12\n1:22018,1,12,8\n''') - else: - self.do_run(src, '''*16*\n0:22016,0,32,48\n1:22018,1,48,32\n''') - - def test_tinyfuncstr(self): - if self.emcc_args is None: return self.skip('requires emcc') - src = ''' - #include - - struct Class { - static char *name1() { return "nameA"; } - char *name2() { return "nameB"; } - }; - - int main() { - printf("*%s,%s*\\n", Class::name1(), (new Class())->name2()); - return 0; - } - ''' - self.do_run(src, '*nameA,nameB*') - - def test_llvmswitch(self): - Settings.CORRECT_SIGNS = 1 - - src = ''' - #include - #include - - int switcher(int p) - { - switch(p) { - case 'a': - case 'b': - case 'c': - return p-1; - case -15: - return p+1; - } - return p; - } - - int main( int argc, const char *argv[] ) { - unsigned int x = 0xfffffff1; - x >>= (argc-1); // force it to be unsigned for purpose of checking our switch comparison in signed/unsigned - printf("*%d,%d,%d,%d,%d,%d*\\n", switcher('a'), switcher('b'), switcher('c'), switcher(x), switcher(-15), switcher('e')); - return 0; - } - ''' - self.do_run(src, '*96,97,98,-14,-14,101*') - - # By default, when user has not specified a -std flag, Emscripten should always build .cpp files using the C++03 standard, - # i.e. as if "-std=c++03" had been passed on the command line. On Linux with Clang 3.2 this is the case, but on Windows - # with Clang 3.2 -std=c++11 has been chosen as default, because of - # < jrose> clb: it's deliberate, with the idea that for people who don't care about the standard, they should be using the "best" thing we can offer on that platform - def test_cxx03_do_run(self): - src = ''' - #include - - #if __cplusplus != 199711L - #error By default, if no -std is specified, emscripten should be compiling with -std=c++03! - #endif - - int main( int argc, const char *argv[] ) { - printf("Hello world!\\n"); - return 0; - } - ''' - self.do_run(src, 'Hello world!') - - def test_bigswitch(self): - if Settings.RELOOP: return self.skip('TODO: switch in relooper, issue #781') - if Settings.ASM_JS: return self.skip('TODO: switch too large for asm') - - src = open(path_from_root('tests', 'bigswitch.cpp')).read() - self.do_run(src, '''34962: GL_ARRAY_BUFFER (0x8892) -26214: what? -35040: GL_STREAM_DRAW (0x88E0) -''', args=['34962', '26214', '35040']) - - def test_indirectbr(self): - Building.COMPILER_TEST_OPTS = filter(lambda x: x != '-g', Building.COMPILER_TEST_OPTS) - - src = ''' - #include - int main(void) { - const void *addrs[2] = { &&FOO, &&BAR }; - - // confuse the optimizer so it doesn't hardcode the jump and avoid generating an |indirectbr| instruction - int which = 0; - for (int x = 0; x < 1000; x++) which = (which + x*x) % 7; - which = (which % 2) + 1; - - goto *addrs[which]; - - FOO: - printf("bad\\n"); - return 0; - BAR: - printf("good\\n"); - const void *addr = &&FOO; - goto *addr; - } - ''' - self.do_run(src, 'good\nbad') - - def test_indirectbr_many(self): - if Settings.USE_TYPED_ARRAYS != 2: return self.skip('blockaddr > 255 requires ta2') - - blocks = range(1500) - init = ', '.join(['&&B%d' % b for b in blocks]) - defs = '\n'.join(['B%d: printf("%d\\n"); return 0;' % (b,b) for b in blocks]) - src = ''' - #include - int main(int argc, char **argv) { - printf("\\n"); - const void *addrs[] = { %s }; - goto *addrs[argc*argc + 1000]; - -%s - return 0; - } - ''' % (init, defs) - self.do_run(src, '\n1001\n') - - def test_pack(self): - src = ''' - #include - #include - - #pragma pack(push,1) - typedef struct header - { - unsigned char id; - unsigned short colour; - unsigned char desc; - } header; - #pragma pack(pop) - - typedef struct fatheader - { - unsigned char id; - unsigned short colour; - unsigned char desc; - } fatheader; - - int main( int argc, const char *argv[] ) { - header h, *ph = 0; - fatheader fh, *pfh = 0; - printf("*%d,%d,%d*\\n", sizeof(header), (int)((int)&h.desc - (int)&h.id), (int)(&ph[1])-(int)(&ph[0])); - printf("*%d,%d,%d*\\n", sizeof(fatheader), (int)((int)&fh.desc - (int)&fh.id), (int)(&pfh[1])-(int)(&pfh[0])); - return 0; - } - ''' - if Settings.QUANTUM_SIZE == 1: - self.do_run(src, '*4,2,3*\n*6,2,3*') - else: - self.do_run(src, '*4,3,4*\n*6,4,6*') - - def test_varargs(self): - if Settings.QUANTUM_SIZE == 1: return self.skip('FIXME: Add support for this') - if not self.is_le32(): return self.skip('we do not support all varargs stuff without le32') - - src = ''' - #include - #include - - void vary(const char *s, ...) - { - va_list v; - va_start(v, s); - char d[20]; - vsnprintf(d, 20, s, v); - puts(d); - - // Try it with copying - va_list tempva; - va_copy(tempva, v); - vsnprintf(d, 20, s, tempva); - puts(d); - - va_end(v); - } - - void vary2(char color, const char *s, ...) - { - va_list v; - va_start(v, s); - char d[21]; - d[0] = color; - vsnprintf(d+1, 20, s, v); - puts(d); - va_end(v); - } - - void varargs_listoffsets_list_evaluate(int count, va_list ap, int vaIteration) - { - while(count > 0) - { - const char* string = va_arg(ap, const char*); - printf("%s", string); - count--; - } - printf("\\n"); - } - - void varags_listoffsets_list_copy(int count, va_list ap, int iteration) - { - va_list ap_copy; - va_copy(ap_copy, ap); - varargs_listoffsets_list_evaluate(count, ap_copy, iteration); - va_end(ap_copy); - } - - void varargs_listoffsets_args(int type, int count, ...) - { - va_list ap; - va_start(ap, count); - - // evaluate a copied list - varags_listoffsets_list_copy(count, ap, 1); - varags_listoffsets_list_copy(count, ap, 2); - varags_listoffsets_list_copy(count, ap, 3); - varags_listoffsets_list_copy(count, ap, 4); - - varargs_listoffsets_list_evaluate(count, ap, 1); - - // NOTE: we expect this test to fail, so we will check the stdout for ..... - varargs_listoffsets_list_evaluate(count, ap, 2); - - // NOTE: this test has to work again, as we restart the list - va_end(ap); - va_start(ap, count); - varargs_listoffsets_list_evaluate(count, ap, 3); - va_end(ap); - } - - void varargs_listoffsets_main() - { - varargs_listoffsets_args(0, 5, "abc", "def", "ghi", "jkl", "mno", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""); - } - - #define GETMAX(pref, type) \ - type getMax##pref(int num, ...) \ - { \ - va_list vv; \ - va_start(vv, num); \ - type maxx = va_arg(vv, type); \ - for (int i = 1; i < num; i++) \ - { \ - type curr = va_arg(vv, type); \ - maxx = curr > maxx ? curr : maxx; \ - } \ - va_end(vv); \ - return maxx; \ - } - GETMAX(i, int); - GETMAX(D, double); - - int main(int argc, char **argv) { - vary("*cheez: %d+%d*", 0, 24); // Also tests that '0' is not special as an array ender - vary("*albeit*"); // Should not fail with no var args in vararg function - vary2('Q', "%d*", 85); - - int maxxi = getMaxi(6, 2, 5, 21, 4, -10, 19); - printf("maxxi:%d*\\n", maxxi); - double maxxD = getMaxD(6, (double)2.1, (double)5.1, (double)22.1, (double)4.1, (double)-10.1, (double)19.1, (double)2); - printf("maxxD:%.2f*\\n", (float)maxxD); - - // And, as a function pointer - void (*vfp)(const char *s, ...) = argc == 1211 ? NULL : vary; - vfp("*vfp:%d,%d*", 22, 199); - - // ensure lists work properly when copied, reinited etc. - varargs_listoffsets_main(); - - return 0; - } - ''' - self.do_run(src, '*cheez: 0+24*\n*cheez: 0+24*\n*albeit*\n*albeit*\nQ85*\nmaxxi:21*\nmaxxD:22.10*\n*vfp:22,199*\n*vfp:22,199*\n'+ - 'abcdefghijklmno\nabcdefghijklmno\nabcdefghijklmno\nabcdefghijklmno\nabcdefghijklmno\n\nabcdefghijklmno\n') - - def test_varargs_byval(self): - if Settings.USE_TYPED_ARRAYS != 2: return self.skip('FIXME: Add support for this') - if self.is_le32(): return self.skip('clang cannot compile this code with that target yet') - - src = r''' - #include - #include - - typedef struct type_a { - union { - double f; - void *p; - int i; - short sym; - } value; - } type_a; - - enum mrb_vtype { - MRB_TT_FALSE = 0, /* 0 */ - MRB_TT_CLASS = 9 /* 9 */ - }; - - typedef struct type_b { - enum mrb_vtype tt:8; - } type_b; - - void print_type_a(int argc, ...); - void print_type_b(int argc, ...); - - int main(int argc, char *argv[]) - { - type_a a; - type_b b; - a.value.p = (void*) 0x12345678; - b.tt = MRB_TT_CLASS; - - printf("The original address of a is: %p\n", a.value.p); - printf("The original type of b is: %d\n", b.tt); - - print_type_a(1, a); - print_type_b(1, b); - - return 0; - } - - void print_type_a(int argc, ...) { - va_list ap; - type_a a; - - va_start(ap, argc); - a = va_arg(ap, type_a); - va_end(ap); - - printf("The current address of a is: %p\n", a.value.p); - } - - void print_type_b(int argc, ...) { - va_list ap; - type_b b; - - va_start(ap, argc); - b = va_arg(ap, type_b); - va_end(ap); - - printf("The current type of b is: %d\n", b.tt); - } - ''' - self.do_run(src, '''The original address of a is: 0x12345678 -The original type of b is: 9 -The current address of a is: 0x12345678 -The current type of b is: 9 -''') - - def test_functionpointer_libfunc_varargs(self): - src = r''' - #include - #include - typedef int (*fp_t)(int, int, ...); - int main(int argc, char **argv) { - fp_t fp = &fcntl; - if (argc == 1337) fp = (fp_t)&main; - (*fp)(0, 10); - (*fp)(0, 10, 5); - printf("waka\n"); - return 0; - } - ''' - self.do_run(src, '''waka''') - - def test_structbyval(self): - Settings.INLINING_LIMIT = 50 - - # part 1: make sure that normally, passing structs by value works - - src = r''' - #include - - struct point - { - int x, y; - }; - - void dump(struct point p) { - p.x++; // should not modify - p.y++; // anything in the caller! - printf("dump: %d,%d\n", p.x, p.y); - } - - void dumpmod(struct point *p) { - p->x++; // should not modify - p->y++; // anything in the caller! - printf("dump: %d,%d\n", p->x, p->y); - } - - int main( int argc, const char *argv[] ) { - point p = { 54, 2 }; - printf("pre: %d,%d\n", p.x, p.y); - dump(p); - void (*dp)(point p) = dump; // And, as a function pointer - dp(p); - printf("post: %d,%d\n", p.x, p.y); - dumpmod(&p); - dumpmod(&p); - printf("last: %d,%d\n", p.x, p.y); - return 0; - } - ''' - self.do_run(src, 'pre: 54,2\ndump: 55,3\ndump: 55,3\npost: 54,2\ndump: 55,3\ndump: 56,4\nlast: 56,4') - - # Check for lack of warning in the generated code (they should appear in part 2) - generated = open(os.path.join(self.get_dir(), 'src.cpp.o.js')).read() - assert 'Casting a function pointer type to another with a different number of arguments.' not in generated, 'Unexpected warning' - - # part 2: make sure we warn about mixing c and c++ calling conventions here - - if not (self.emcc_args is None or self.emcc_args == []): return # Optimized code is missing the warning comments - - header = r''' - struct point - { - int x, y; - }; - - ''' - open(os.path.join(self.get_dir(), 'header.h'), 'w').write(header) - - supp = r''' - #include - #include "header.h" - - void dump(struct point p) { - p.x++; // should not modify - p.y++; // anything in the caller! - printf("dump: %d,%d\n", p.x, p.y); - } - ''' - supp_name = os.path.join(self.get_dir(), 'supp.c') - open(supp_name, 'w').write(supp) - - main = r''' - #include - #include "header.h" - - #ifdef __cplusplus - extern "C" { - #endif - void dump(struct point p); - #ifdef __cplusplus - } - #endif - - int main( int argc, const char *argv[] ) { - struct point p = { 54, 2 }; - printf("pre: %d,%d\n", p.x, p.y); - dump(p); - void (*dp)(struct point p) = dump; // And, as a function pointer - dp(p); - printf("post: %d,%d\n", p.x, p.y); - return 0; - } - ''' - main_name = os.path.join(self.get_dir(), 'main.cpp') - open(main_name, 'w').write(main) - - Building.emcc(supp_name) - Building.emcc(main_name) - all_name = os.path.join(self.get_dir(), 'all.bc') - Building.link([supp_name + '.o', main_name + '.o'], all_name) - - # This will fail! See explanation near the warning we check for, in the compiler source code - output = Popen([PYTHON, EMCC, all_name], stderr=PIPE).communicate() - - # Check for warning in the generated code - generated = open(os.path.join(self.get_dir(), 'src.cpp.o.js')).read() - if 'i386-pc-linux-gnu' in COMPILER_OPTS: - assert 'Casting a function pointer type to a potentially incompatible one' in output[1], 'Missing expected warning' - else: - print >> sys.stderr, 'skipping C/C++ conventions warning check, since not i386-pc-linux-gnu' - - def test_stdlibs(self): - if self.emcc_args is None: return self.skip('requires emcc') - if Settings.USE_TYPED_ARRAYS == 2: - # Typed arrays = 2 + safe heap prints a warning that messes up our output. - Settings.SAFE_HEAP = 0 - src = ''' - #include - #include - #include - - void clean() - { - printf("*cleaned*\\n"); - } - - int comparer(const void *a, const void *b) { - int aa = *((int*)a); - int bb = *((int*)b); - return aa - bb; - } - - int main() { - // timeofday - timeval t; - gettimeofday(&t, NULL); - printf("*%d,%d\\n", int(t.tv_sec), int(t.tv_usec)); // should not crash - - // atexit - atexit(clean); - - // qsort - int values[6] = { 3, 2, 5, 1, 5, 6 }; - qsort(values, 5, sizeof(int), comparer); - printf("*%d,%d,%d,%d,%d,%d*\\n", values[0], values[1], values[2], values[3], values[4], values[5]); - - printf("*stdin==0:%d*\\n", stdin == 0); // check that external values are at least not NULL - printf("*%%*\\n"); - printf("*%.1ld*\\n", 5); - - printf("*%.1f*\\n", strtod("66", NULL)); // checks dependency system, as our strtod needs _isspace etc. - - printf("*%ld*\\n", strtol("10", NULL, 0)); - printf("*%ld*\\n", strtol("0", NULL, 0)); - printf("*%ld*\\n", strtol("-10", NULL, 0)); - printf("*%ld*\\n", strtol("12", NULL, 16)); - - printf("*%lu*\\n", strtoul("10", NULL, 0)); - printf("*%lu*\\n", strtoul("0", NULL, 0)); - printf("*%lu*\\n", strtoul("-10", NULL, 0)); - - printf("*malloc(0)!=0:%d*\\n", malloc(0) != 0); // We should not fail horribly - - return 0; - } - ''' - - self.do_run(src, '*1,2,3,5,5,6*\n*stdin==0:0*\n*%*\n*5*\n*66.0*\n*10*\n*0*\n*-10*\n*18*\n*10*\n*0*\n*4294967286*\n*malloc(0)!=0:1*\n*cleaned*') - - src = r''' - #include - #include - - int main() { - bool x = true; - bool y = false; - printf("*%d*\n", x != y); - return 0; - } - ''' - - self.do_run(src, '*1*', force_c=True) - - def test_strtoll_hex(self): - if self.emcc_args is None: return self.skip('requires emcc') - - # tests strtoll for hex strings (0x...) - src = r''' - #include - #include - - int main() { - const char *STRING = "0x4 -0x3A +0xDEADBEEF"; - char *end_char; - - // undefined base - long long int l1 = strtoll(STRING, &end_char, 0); - long long int l2 = strtoll(end_char, &end_char, 0); - long long int l3 = strtoll(end_char, NULL, 0); - - // defined base - long long int l4 = strtoll(STRING, &end_char, 16); - long long int l5 = strtoll(end_char, &end_char, 16); - long long int l6 = strtoll(end_char, NULL, 16); - - printf("%d%d%d%d%d%d\n", l1==0x4, l2==-0x3a, l3==0xdeadbeef, l4==0x4, l5==-0x3a, l6==0xdeadbeef); - return 0; - } - ''' - self.do_run(src, '111111') - - def test_strtoll_dec(self): - if self.emcc_args is None: return self.skip('requires emcc') - - # tests strtoll for decimal strings (0x...) - src = r''' - #include - #include - - int main() { - const char *STRING = "4 -38 +4711"; - char *end_char; - - // undefined base - long long int l1 = strtoll(STRING, &end_char, 0); - long long int l2 = strtoll(end_char, &end_char, 0); - long long int l3 = strtoll(end_char, NULL, 0); - - // defined base - long long int l4 = strtoll(STRING, &end_char, 10); - long long int l5 = strtoll(end_char, &end_char, 10); - long long int l6 = strtoll(end_char, NULL, 10); - - printf("%d%d%d%d%d%d\n", l1==4, l2==-38, l3==4711, l4==4, l5==-38, l6==4711); - return 0; - } - ''' - self.do_run(src, '111111') - - def test_strtoll_bin(self): - if self.emcc_args is None: return self.skip('requires emcc') - - # tests strtoll for binary strings (0x...) - src = r''' - #include - #include - - int main() { - const char *STRING = "1 -101 +1011"; - char *end_char; - - // defined base - long long int l4 = strtoll(STRING, &end_char, 2); - long long int l5 = strtoll(end_char, &end_char, 2); - long long int l6 = strtoll(end_char, NULL, 2); - - printf("%d%d%d\n", l4==1, l5==-5, l6==11); - return 0; - } - ''' - self.do_run(src, '111') - - def test_strtoll_oct(self): - if self.emcc_args is None: return self.skip('requires emcc') - - # tests strtoll for decimal strings (0x...) - src = r''' - #include - #include - - int main() { - const char *STRING = "0 -035 +04711"; - char *end_char; - - // undefined base - long long int l1 = strtoll(STRING, &end_char, 0); - long long int l2 = strtoll(end_char, &end_char, 0); - long long int l3 = strtoll(end_char, NULL, 0); - - // defined base - long long int l4 = strtoll(STRING, &end_char, 8); - long long int l5 = strtoll(end_char, &end_char, 8); - long long int l6 = strtoll(end_char, NULL, 8); - - printf("%d%d%d%d%d%d\n", l1==0, l2==-29, l3==2505, l4==0, l5==-29, l6==2505); - return 0; - } - ''' - self.do_run(src, '111111') - - def test_strtol_hex(self): - # tests strtoll for hex strings (0x...) - src = r''' - #include - #include - - int main() { - const char *STRING = "0x4 -0x3A +0xDEAD"; - char *end_char; - - // undefined base - long l1 = strtol(STRING, &end_char, 0); - long l2 = strtol(end_char, &end_char, 0); - long l3 = strtol(end_char, NULL, 0); - - // defined base - long l4 = strtol(STRING, &end_char, 16); - long l5 = strtol(end_char, &end_char, 16); - long l6 = strtol(end_char, NULL, 16); - - printf("%d%d%d%d%d%d\n", l1==0x4, l2==-0x3a, l3==0xdead, l4==0x4, l5==-0x3a, l6==0xdead); - return 0; - } - ''' - self.do_run(src, '111111') - - def test_strtol_dec(self): - # tests strtoll for decimal strings (0x...) - src = r''' - #include - #include - - int main() { - const char *STRING = "4 -38 +4711"; - char *end_char; - - // undefined base - long l1 = strtol(STRING, &end_char, 0); - long l2 = strtol(end_char, &end_char, 0); - long l3 = strtol(end_char, NULL, 0); - - // defined base - long l4 = strtol(STRING, &end_char, 10); - long l5 = strtol(end_char, &end_char, 10); - long l6 = strtol(end_char, NULL, 10); - - printf("%d%d%d%d%d%d\n", l1==4, l2==-38, l3==4711, l4==4, l5==-38, l6==4711); - return 0; - } - ''' - self.do_run(src, '111111') - - def test_strtol_bin(self): - # tests strtoll for binary strings (0x...) - src = r''' - #include - #include - - int main() { - const char *STRING = "1 -101 +1011"; - char *end_char; - - // defined base - long l4 = strtol(STRING, &end_char, 2); - long l5 = strtol(end_char, &end_char, 2); - long l6 = strtol(end_char, NULL, 2); - - printf("%d%d%d\n", l4==1, l5==-5, l6==11); - return 0; - } - ''' - self.do_run(src, '111') - - def test_strtol_oct(self): - # tests strtoll for decimal strings (0x...) - src = r''' - #include - #include - - int main() { - const char *STRING = "0 -035 +04711"; - char *end_char; - - // undefined base - long l1 = strtol(STRING, &end_char, 0); - long l2 = strtol(end_char, &end_char, 0); - long l3 = strtol(end_char, NULL, 0); - - // defined base - long l4 = strtol(STRING, &end_char, 8); - long l5 = strtol(end_char, &end_char, 8); - long l6 = strtol(end_char, NULL, 8); - - printf("%d%d%d%d%d%d\n", l1==0, l2==-29, l3==2505, l4==0, l5==-29, l6==2505); - return 0; - } - ''' - self.do_run(src, '111111') - - def test_atexit(self): - # Confirms they are called in reverse order - src = r''' - #include - #include - - static void cleanA() { - printf("A"); - } - static void cleanB() { - printf("B"); - } - - int main() { - atexit(cleanA); - atexit(cleanB); - return 0; - } - ''' - self.do_run(src, 'BA') - - def test_time(self): - # XXX Not sure what the right output is here. Looks like the test started failing with daylight savings changes. Modified it to pass again. - src = open(path_from_root('tests', 'time', 'src.c'), 'r').read() - expected = open(path_from_root('tests', 'time', 'output.txt'), 'r').read() - expected2 = open(path_from_root('tests', 'time', 'output2.txt'), 'r').read() - self.do_run(src, [expected, expected2], - extra_emscripten_args=['-H', 'libc/time.h']) - #extra_emscripten_args=['-H', 'libc/fcntl.h,libc/sys/unistd.h,poll.h,libc/math.h,libc/langinfo.h,libc/time.h']) - - def test_timeb(self): - # Confirms they are called in reverse order - src = r''' - #include - #include - #include - - int main() { - timeb tb; - tb.timezone = 1; - printf("*%d\n", ftime(&tb)); - assert(tb.time > 10000); - assert(tb.timezone == 0); - assert(tb.dstflag == 0); - return 0; - } - ''' - self.do_run(src, '*0\n') - - def test_time_c(self): - src = r''' - #include - #include - - int main() { - time_t t = time(0); - printf("time: %s\n", ctime(&t)); - } - ''' - self.do_run(src, 'time: ') # compilation check, mainly - - def test_gmtime(self): - src = r''' - #include - #include - #include - #include - - int main(void) - { - time_t t=time(NULL); - struct tm *ptm=gmtime(&t); - struct tm tmCurrent=*ptm; - int hour=tmCurrent.tm_hour; - - t-=hour*3600; // back to midnight - int yday = -1; - for(hour=0;hour<24;hour++) - { - ptm=gmtime(&t); - // tm_yday must be constant all day... - printf("yday: %d, hour: %d\n", ptm->tm_yday, hour); - if (yday == -1) yday = ptm->tm_yday; - else assert(yday == ptm->tm_yday); - t+=3600; // add one hour - } - printf("ok!\n"); - return(0); - } - ''' - self.do_run(src, '''ok!''') - - def test_strptime_tm(self): - src=r''' - #include - #include - #include - - int main() { - struct tm tm; - char *ptr = strptime("17410105012000", "%H%M%S%d%m%Y", &tm); - - printf("%s: %s, %d/%d/%d %d:%d:%d", - (ptr != NULL && *ptr=='\0') ? "OK" : "ERR", - tm.tm_wday == 0 ? "Sun" : (tm.tm_wday == 1 ? "Mon" : (tm.tm_wday == 2 ? "Tue" : (tm.tm_wday == 3 ? "Wed" : (tm.tm_wday == 4 ? "Thu" : (tm.tm_wday == 5 ? "Fri" : (tm.tm_wday == 6 ? "Sat" : "ERR")))))), - tm.tm_mon+1, - tm.tm_mday, - tm.tm_year+1900, - tm.tm_hour, - tm.tm_min, - tm.tm_sec - ); - } - ''' - self.do_run(src, 'OK: Wed, 1/5/2000 17:41:1') - - def test_strptime_days(self): - src = r''' - #include - #include - #include - - static const struct { - const char *input; - const char *format; - } day_tests[] = { - { "2000-01-01", "%Y-%m-%d"}, - { "03/03/00", "%D"}, - { "9/9/99", "%x"}, - { "19990502123412", "%Y%m%d%H%M%S"}, - { "2001 20 Mon", "%Y %U %a"}, - { "2006 4 Fri", "%Y %U %a"}, - { "2001 21 Mon", "%Y %W %a"}, - { "2013 29 Wed", "%Y %W %a"}, - { "2000-01-01 08:12:21 AM", "%Y-%m-%d %I:%M:%S %p"}, - { "2000-01-01 08:12:21 PM", "%Y-%m-%d %I:%M:%S %p"}, - { "2001 17 Tue", "%Y %U %a"}, - { "2001 8 Thursday", "%Y %W %a"}, - }; - - int main() { - struct tm tm; - - for (int i = 0; i < sizeof (day_tests) / sizeof (day_tests[0]); ++i) { - memset (&tm, '\0', sizeof (tm)); - char *ptr = strptime(day_tests[i].input, day_tests[i].format, &tm); - - printf("%s: %d/%d/%d (%dth DoW, %dth DoY)\n", (ptr != NULL && *ptr=='\0') ? "OK" : "ERR", tm.tm_mon+1, tm.tm_mday, 1900+tm.tm_year, tm.tm_wday, tm.tm_yday); - } - } - ''' - self.do_run(src, 'OK: 1/1/2000 (6th DoW, 0th DoY)\n'\ - 'OK: 3/3/2000 (5th DoW, 62th DoY)\n'\ - 'OK: 9/9/1999 (4th DoW, 251th DoY)\n'\ - 'OK: 5/2/1999 (0th DoW, 121th DoY)\n'\ - 'OK: 5/21/2001 (1th DoW, 140th DoY)\n'\ - 'OK: 1/27/2006 (5th DoW, 26th DoY)\n'\ - 'OK: 5/21/2001 (1th DoW, 140th DoY)\n'\ - 'OK: 7/24/2013 (3th DoW, 204th DoY)\n'\ - 'OK: 1/1/2000 (6th DoW, 0th DoY)\n'\ - 'OK: 1/1/2000 (6th DoW, 0th DoY)\n'\ - 'OK: 5/1/2001 (2th DoW, 120th DoY)\n'\ - 'OK: 2/22/2001 (4th DoW, 52th DoY)\n'\ - ) - - def test_strptime_reentrant(self): - src=r''' - #include - #include - #include - #include - - int main () { - int result = 0; - struct tm tm; - - memset (&tm, 0xaa, sizeof (tm)); - - /* Test we don't crash on uninitialized struct tm. - Some fields might contain bogus values until everything - needed is initialized, but we shouldn't crash. */ - if (strptime ("2007", "%Y", &tm) == NULL - || strptime ("12", "%d", &tm) == NULL - || strptime ("Feb", "%b", &tm) == NULL - || strptime ("13", "%M", &tm) == NULL - || strptime ("21", "%S", &tm) == NULL - || strptime ("16", "%H", &tm) == NULL) { - printf("ERR: returned NULL"); - exit(EXIT_FAILURE); - } - - if (tm.tm_sec != 21 || tm.tm_min != 13 || tm.tm_hour != 16 - || tm.tm_mday != 12 || tm.tm_mon != 1 || tm.tm_year != 107 - || tm.tm_wday != 1 || tm.tm_yday != 42) { - printf("ERR: unexpected tm content (1) - %d/%d/%d %d:%d:%d", tm.tm_mon+1, tm.tm_mday, tm.tm_year+1900, tm.tm_hour, tm.tm_min, tm.tm_sec); - exit(EXIT_FAILURE); - } - - if (strptime ("8", "%d", &tm) == NULL) { - printf("ERR: strptime failed"); - exit(EXIT_FAILURE); - } - - if (tm.tm_sec != 21 || tm.tm_min != 13 || tm.tm_hour != 16 - || tm.tm_mday != 8 || tm.tm_mon != 1 || tm.tm_year != 107 - || tm.tm_wday != 4 || tm.tm_yday != 38) { - printf("ERR: unexpected tm content (2) - %d/%d/%d %d:%d:%d", tm.tm_mon+1, tm.tm_mday, tm.tm_year+1900, tm.tm_hour, tm.tm_min, tm.tm_sec); - exit(EXIT_FAILURE); - } - - printf("OK"); - } - ''' - self.do_run(src, 'OK') - - def test_strftime(self): - src=r''' - #include - #include - #include - #include - - void test(int result, const char* comment, const char* parsed = "") { - printf("%d",result); - if (!result) { - printf("\nERROR: %s (\"%s\")\n", comment, parsed); - } - } - - int cmp(const char *s1, const char *s2) { - for ( ; *s1 == *s2 ; s1++,s2++ ) { - if ( *s1 == '\0' ) - break; - } - - return (*s1 - *s2); - } - - int main() { - struct tm tm; - char s[1000]; - size_t size; - - tm.tm_sec = 4; - tm.tm_min = 23; - tm.tm_hour = 20; - tm.tm_mday = 21; - tm.tm_mon = 1; - tm.tm_year = 74; - tm.tm_wday = 4; - tm.tm_yday = 51; - tm.tm_isdst = 0; - - size = strftime(s, 1000, "", &tm); - test((size==0) && (*s=='\0'), "strftime test #1", s); - - size = strftime(s, 1000, "%a", &tm); - test((size==3) && !cmp(s, "Thu"), "strftime test #2", s); - - size = strftime(s, 1000, "%A", &tm); - test((size==8) && !cmp(s, "Thursday"), "strftime test #3", s); - - size = strftime(s, 1000, "%b", &tm); - test((size==3) && !cmp(s, "Feb"), "strftime test #4", s); - - size = strftime(s, 1000, "%B", &tm); - test((size==8) && !cmp(s, "February"), - "strftime test #5", s); - - size = strftime(s, 1000, "%d", &tm); - test((size==2) && !cmp(s, "21"), - "strftime test #6", s); - - size = strftime(s, 1000, "%H", &tm); - test((size==2) && !cmp(s, "20"), - "strftime test #7", s); - - size = strftime(s, 1000, "%I", &tm); - test((size==2) && !cmp(s, "08"), - "strftime test #8", s); - - size = strftime(s, 1000, "%j", &tm); - test((size==3) && !cmp(s, "052"), - "strftime test #9", s); - - size = strftime(s, 1000, "%m", &tm); - test((size==2) && !cmp(s, "02"), - "strftime test #10", s); - - size = strftime(s, 1000, "%M", &tm); - test((size==2) && !cmp(s, "23"), - "strftime test #11", s); - - size = strftime(s, 1000, "%p", &tm); - test((size==2) && !cmp(s, "PM"), - "strftime test #12", s); - - size = strftime(s, 1000, "%S", &tm); - test((size==2) && !cmp(s, "04"), - "strftime test #13", s); - - size = strftime(s, 1000, "%U", &tm); - test((size==2) && !cmp(s, "07"), - "strftime test #14", s); - - size = strftime(s, 1000, "%w", &tm); - test((size==1) && !cmp(s, "4"), - "strftime test #15", s); - - size = strftime(s, 1000, "%W", &tm); - test((size==2) && !cmp(s, "07"), - "strftime test #16", s); - - size = strftime(s, 1000, "%y", &tm); - test((size==2) && !cmp(s, "74"), - "strftime test #17", s); - - size = strftime(s, 1000, "%Y", &tm); - test((size==4) && !cmp(s, "1974"), - "strftime test #18", s); - - size = strftime(s, 1000, "%%", &tm); - test((size==1) && !cmp(s, "%"), - "strftime test #19", s); - - size = strftime(s, 5, "%Y", &tm); - test((size==4) && !cmp(s, "1974"), - "strftime test #20", s); - - size = strftime(s, 4, "%Y", &tm); - test((size==0), "strftime test #21", s); - - tm.tm_mon = 0; - tm.tm_mday = 1; - size = strftime(s, 10, "%U", &tm); - test((size==2) && !cmp(s, "00"), "strftime test #22", s); - - size = strftime(s, 10, "%W", &tm); - test((size==2) && !cmp(s, "00"), "strftime test #23", s); - - // 1/1/1973 was a Sunday and is in CW 1 - tm.tm_year = 73; - size = strftime(s, 10, "%W", &tm); - test((size==2) && !cmp(s, "01"), "strftime test #24", s); - - // 1/1/1978 was a Monday and is in CW 1 - tm.tm_year = 78; - size = strftime(s, 10, "%U", &tm); - test((size==2) && !cmp(s, "01"), "strftime test #25", s); - - // 2/1/1999 - tm.tm_year = 99; - tm.tm_yday = 1; - size = strftime(s, 10, "%G (%V)", &tm); - test((size==9) && !cmp(s, "1998 (53)"), "strftime test #26", s); - - size = strftime(s, 10, "%g", &tm); - test((size==2) && !cmp(s, "98"), "strftime test #27", s); - - // 30/12/1997 - tm.tm_year = 97; - tm.tm_yday = 363; - size = strftime(s, 10, "%G (%V)", &tm); - test((size==9) && !cmp(s, "1998 (01)"), "strftime test #28", s); - - size = strftime(s, 10, "%g", &tm); - test((size==2) && !cmp(s, "98"), "strftime test #29", s); - } - ''' - self.do_run(src, '11111111111111111111111111111') - - def test_intentional_fault(self): - # Some programs intentionally segfault themselves, we should compile that into a throw - src = r''' - int main () { - *(volatile char *)0 = 0; - return 0; - } - ''' - self.do_run(src, 'fault on write to 0' if not Settings.ASM_JS else 'abort()') - - def test_trickystring(self): - src = r''' - #include - - typedef struct - { - int (*f)(void *); - void *d; - char s[16]; - } LMEXFunctionStruct; - - int f(void *user) - { - return 0; - } - - static LMEXFunctionStruct const a[] = - { - {f, (void *)(int)'a', "aa"} - }; - - int main() - { - printf("ok\n"); - return a[0].f(a[0].d); - } - ''' - self.do_run(src, 'ok\n') - - def test_statics(self): - # static initializers save i16 but load i8 for some reason (or i64 and load i8) - if Settings.SAFE_HEAP: - Settings.SAFE_HEAP = 3 - Settings.SAFE_HEAP_LINES = ['src.cpp:19', 'src.cpp:26', 'src.cpp:28'] - - src = ''' - #include - #include - - #define CONSTRLEN 32 - - char * (*func)(char *, const char *) = NULL; - - void conoutfv(const char *fmt) - { - static char buf[CONSTRLEN]; - func(buf, fmt); // call by function pointer to make sure we test strcpy here - puts(buf); - } - - struct XYZ { - float x, y, z; - XYZ(float a, float b, float c) : x(a), y(b), z(c) { } - static const XYZ& getIdentity() - { - static XYZ iT(1,2,3); - return iT; - } - }; - struct S { - static const XYZ& getIdentity() - { - static const XYZ iT(XYZ::getIdentity()); - return iT; - } - }; - - int main() { - func = &strcpy; - conoutfv("*staticccz*"); - printf("*%.2f,%.2f,%.2f*\\n", S::getIdentity().x, S::getIdentity().y, S::getIdentity().z); - return 0; - } - ''' - self.do_run(src, '*staticccz*\n*1.00,2.00,3.00*') - - def test_copyop(self): - if self.emcc_args is None: return self.skip('requires emcc') - - # clang generated code is vulnerable to this, as it uses - # memcpy for assignments, with hardcoded numbers of bytes - # (llvm-gcc copies items one by one). See QUANTUM_SIZE in - # settings.js. - src = ''' - #include - #include - #include - - struct vec { - double x,y,z; - vec() : x(0), y(0), z(0) { }; - vec(const double a, const double b, const double c) : x(a), y(b), z(c) { }; - }; - - struct basis { - vec a, b, c; - basis(const vec& v) { - a=v; // should not touch b! - printf("*%.2f,%.2f,%.2f*\\n", b.x, b.y, b.z); - } - }; - - int main() { - basis B(vec(1,0,0)); - - // Part 2: similar problem with memset and memmove - int x = 1, y = 77, z = 2; - memset((void*)&x, 0, sizeof(int)); - memset((void*)&z, 0, sizeof(int)); - printf("*%d,%d,%d*\\n", x, y, z); - memcpy((void*)&x, (void*)&z, sizeof(int)); - memcpy((void*)&z, (void*)&x, sizeof(int)); - printf("*%d,%d,%d*\\n", x, y, z); - memmove((void*)&x, (void*)&z, sizeof(int)); - memmove((void*)&z, (void*)&x, sizeof(int)); - printf("*%d,%d,%d*\\n", x, y, z); - return 0; - } - ''' - self.do_run(src, '*0.00,0.00,0.00*\n*0,77,0*\n*0,77,0*\n*0,77,0*') - - def test_memcpy_memcmp(self): - src = ''' - #include - #include - #include - - #define MAXX 48 - void reset(unsigned char *buffer) { - for (int i = 0; i < MAXX; i++) buffer[i] = i+1; - } - void dump(unsigned char *buffer) { - for (int i = 0; i < MAXX-1; i++) printf("%2d,", buffer[i]); - printf("%d\\n", buffer[MAXX-1]); - } - int main() { - unsigned char buffer[MAXX]; - for (int i = MAXX/4; i < MAXX-MAXX/4; i++) { - for (int j = MAXX/4; j < MAXX-MAXX/4; j++) { - for (int k = 1; k < MAXX/4; k++) { - if (i == j) continue; - if (i < j && i+k > j) continue; - if (j < i && j+k > i) continue; - printf("[%d,%d,%d] ", i, j, k); - reset(buffer); - memcpy(buffer+i, buffer+j, k); - dump(buffer); - assert(memcmp(buffer+i, buffer+j, k) == 0); - buffer[i + k/2]++; - if (buffer[i + k/2] != 0) { - assert(memcmp(buffer+i, buffer+j, k) > 0); - } else { - assert(memcmp(buffer+i, buffer+j, k) < 0); - } - buffer[i + k/2]--; - buffer[j + k/2]++; - if (buffer[j + k/2] != 0) { - assert(memcmp(buffer+i, buffer+j, k) < 0); - } else { - assert(memcmp(buffer+i, buffer+j, k) > 0); - } - } - } - } - return 0; - } - ''' - def check(result, err): - return hashlib.sha1(result).hexdigest() - self.do_run(src, '6c9cdfe937383b79e52ca7a2cce83a21d9f5422c', - output_nicerizer = check) - - def test_memcpy2(self): - src = r''' - #include - #include - #include - int main() { - char buffer[256]; - for (int i = 0; i < 10; i++) { - for (int j = 0; j < 10; j++) { - for (int k = 0; k < 35; k++) { - for (int t = 0; t < 256; t++) buffer[t] = t; - char *dest = buffer + i + 128; - char *src = buffer+j; - //printf("%d, %d, %d\n", i, j, k); - assert(memcpy(dest, src, k) == dest); - assert(memcmp(dest, src, k) == 0); - } - } - } - printf("ok.\n"); - return 1; - } - ''' - self.do_run(src, 'ok.'); - - def test_getopt(self): - if self.emcc_args is None: return self.skip('needs emcc for libc') - - src = ''' - #pragma clang diagnostic ignored "-Winvalid-pp-token" - #include - #include - #include - - int - main(int argc, char *argv[]) - { - int flags, opt; - int nsecs, tfnd; - - nsecs = 0; - tfnd = 0; - flags = 0; - while ((opt = getopt(argc, argv, "nt:")) != -1) { - switch (opt) { - case 'n': - flags = 1; - break; - case 't': - nsecs = atoi(optarg); - tfnd = 1; - break; - default: /* '?' */ - fprintf(stderr, "Usage: %s [-t nsecs] [-n] name\\n", - argv[0]); - exit(EXIT_FAILURE); - } - } - - printf("flags=%d; tfnd=%d; optind=%d\\n", flags, tfnd, optind); - - if (optind >= argc) { - fprintf(stderr, "Expected argument after options\\n"); - exit(EXIT_FAILURE); - } - - printf("name argument = %s\\n", argv[optind]); - - /* Other code omitted */ - - exit(EXIT_SUCCESS); - } - ''' - self.do_run(src, 'flags=1; tfnd=1; optind=4\nname argument = foobar', args=['-t', '12', '-n', 'foobar']) - - def test_getopt_long(self): - if self.emcc_args is None: return self.skip('needs emcc for libc') - - src = ''' - #pragma clang diagnostic ignored "-Winvalid-pp-token" - #pragma clang diagnostic ignored "-Wdeprecated-writable-strings" - #include /* for printf */ - #include /* for exit */ - #include - - int - main(int argc, char **argv) - { - int c; - int digit_optind = 0; - - while (1) { - int this_option_optind = optind ? optind : 1; - int option_index = 0; - static struct option long_options[] = { - {"add", required_argument, 0, 0 }, - {"append", no_argument, 0, 0 }, - {"delete", required_argument, 0, 0 }, - {"verbose", no_argument, 0, 0 }, - {"create", required_argument, 0, 'c'}, - {"file", required_argument, 0, 0 }, - {0, 0, 0, 0 } - }; - - c = getopt_long(argc, argv, "abc:d:012", - long_options, &option_index); - if (c == -1) - break; - - switch (c) { - case 0: - printf("option %s", long_options[option_index].name); - if (optarg) - printf(" with arg %s", optarg); - printf("\\n"); - break; - - case '0': - case '1': - case '2': - if (digit_optind != 0 && digit_optind != this_option_optind) - printf("digits occur in two different argv-elements.\\n"); - digit_optind = this_option_optind; - printf("option %c\\n", c); - break; - - case 'a': - printf("option a\\n"); - break; - - case 'b': - printf("option b\\n"); - break; - - case 'c': - printf("option c with value '%s'\\n", optarg); - break; - - case 'd': - printf("option d with value '%s'\\n", optarg); - break; - - case '?': - break; - - default: - printf("?? getopt returned character code 0%o ??\\n", c); - } - } - - if (optind < argc) { - printf("non-option ARGV-elements: "); - while (optind < argc) - printf("%s ", argv[optind++]); - printf("\\n"); - } - - exit(EXIT_SUCCESS); - } - ''' - self.do_run(src, 'option file with arg foobar\noption b', args=['--file', 'foobar', '-b']) - - def test_memmove(self): - src = ''' - #include - #include - int main() { - char str[] = "memmove can be very useful....!"; - memmove (str+20, str+15, 11); - puts(str); - return 0; - } - ''' - self.do_run(src, 'memmove can be very very useful') - - def test_memmove2(self): - if Settings.USE_TYPED_ARRAYS != 2: return self.skip('need ta2') - - src = r''' - #include - #include - #include - int main() { - int sum = 0; - char buffer[256]; - for (int i = 0; i < 10; i++) { - for (int j = 0; j < 10; j++) { - for (int k = 0; k < 35; k++) { - for (int t = 0; t < 256; t++) buffer[t] = t; - char *dest = buffer + i; - char *src = buffer + j; - if (dest == src) continue; - //printf("%d, %d, %d\n", i, j, k); - assert(memmove(dest, src, k) == dest); - for (int t = 0; t < 256; t++) sum += buffer[t]; - } - } - } - printf("final: %d.\n", sum); - return 1; - } - ''' - self.do_run(src, 'final: -403200.'); - - def test_memmove3(self): - src = ''' - #include - #include - int main() { - char str[] = "memmove can be vvery useful....!"; - memmove(str+15, str+16, 17); - puts(str); - return 0; - } - ''' - self.do_run(src, 'memmove can be very useful....!') - - def test_bsearch(self): - if Settings.QUANTUM_SIZE == 1: return self.skip('Test cannot work with q1') - - src = ''' - #include - #include - - int cmp(const void* key, const void* member) { - return *(int *)key - *(int *)member; - } - - void printResult(int* needle, int* haystack, unsigned int len) { - void *result = bsearch(needle, haystack, len, sizeof(unsigned int), cmp); - - if (result == NULL) { - printf("null\\n"); - } else { - printf("%d\\n", *(unsigned int *)result); - } - } - - int main() { - int a[] = { -2, -1, 0, 6, 7, 9 }; - int b[] = { 0, 1 }; - - /* Find all keys that exist. */ - for(int i = 0; i < 6; i++) { - int val = a[i]; - - printResult(&val, a, 6); - } - - /* Keys that are covered by the range of the array but aren't in - * the array cannot be found. - */ - int v1 = 3; - int v2 = 8; - printResult(&v1, a, 6); - printResult(&v2, a, 6); - - /* Keys outside the range of the array cannot be found. */ - int v3 = -1; - int v4 = 2; - - printResult(&v3, b, 2); - printResult(&v4, b, 2); - - return 0; - } - ''' - - self.do_run(src, '-2\n-1\n0\n6\n7\n9\nnull\nnull\nnull\nnull') - - def test_nestedstructs(self): - src = ''' - #include - #include "emscripten.h" - - struct base { - int x; - float y; - union { - int a; - float b; - }; - char c; - }; - - struct hashtableentry { - int key; - base data; - }; - - struct hashset { - typedef hashtableentry entry; - struct chain { entry elem; chain *next; }; - // struct chainchunk { chain chains[100]; chainchunk *next; }; - }; - - struct hashtable : hashset { - hashtable() { - base *b = NULL; - entry *e = NULL; - chain *c = NULL; - 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*\\n", - sizeof(base), - int(&(b->x)), int(&(b->y)), int(&(b->a)), int(&(b->b)), int(&(b->c)), - sizeof(hashtableentry), - int(&(e->key)), int(&(e->data)), int(&(e->data.x)), int(&(e->data.y)), int(&(e->data.a)), int(&(e->data.b)), int(&(e->data.c)), - sizeof(hashset::chain), - int(&(c->elem)), int(&(c->next)), int(&(c->elem.key)), int(&(c->elem.data)), int(&(c->elem.data.x)), int(&(c->elem.data.y)), int(&(c->elem.data.a)), int(&(c->elem.data.b)), int(&(c->elem.data.c)) - ); - } - }; - - struct B { char buffer[62]; int last; char laster; char laster2; }; - - struct Bits { - unsigned short A : 1; - unsigned short B : 1; - unsigned short C : 1; - unsigned short D : 1; - unsigned short x1 : 1; - unsigned short x2 : 1; - unsigned short x3 : 1; - unsigned short x4 : 1; - }; - - int main() { - hashtable t; - - // Part 2 - the char[] should be compressed, BUT have a padding space at the end so the next - // one is aligned properly. Also handle char; char; etc. properly. - B *b = NULL; - printf("*%d,%d,%d,%d,%d,%d,%d,%d,%d*\\n", int(b), int(&(b->buffer)), int(&(b->buffer[0])), int(&(b->buffer[1])), int(&(b->buffer[2])), - int(&(b->last)), int(&(b->laster)), int(&(b->laster2)), sizeof(B)); - - // Part 3 - bitfields, and small structures - Bits *b2 = NULL; - printf("*%d*\\n", sizeof(Bits)); - - return 0; - } - ''' - if Settings.QUANTUM_SIZE == 1: - # Compressed memory. Note that sizeof() does give the fat sizes, however! - self.do_run(src, '*16,0,1,2,2,3|20,0,1,1,2,3,3,4|24,0,5,0,1,1,2,3,3,4*\n*0,0,0,1,2,62,63,64,72*\n*2*') - else: - # Bloated memory; same layout as C/C++ - self.do_run(src, '*16,0,4,8,8,12|20,0,4,4,8,12,12,16|24,0,20,0,4,4,8,12,12,16*\n*0,0,0,1,2,64,68,69,72*\n*2*') - - def test_runtimelink(self): - return self.skip('shared libs are deprecated') - if Building.LLVM_OPTS: return self.skip('LLVM opts will optimize printf into puts in the parent, and the child will still look for puts') - if Settings.ASM_JS: return self.skip('asm does not support runtime linking') - - main, supp = self.setup_runtimelink_test() - - self.banned_js_engines = [NODE_JS] # node's global scope behaves differently than everything else, needs investigation FIXME - Settings.LINKABLE = 1 - Settings.BUILD_AS_SHARED_LIB = 2 - Settings.NAMED_GLOBALS = 1 - - self.build(supp, self.get_dir(), self.in_dir('supp.cpp')) - shutil.move(self.in_dir('supp.cpp.o.js'), self.in_dir('liblib.so')) - Settings.BUILD_AS_SHARED_LIB = 0 - - Settings.RUNTIME_LINKED_LIBS = ['liblib.so']; - self.do_run(main, 'supp: 54,2\nmain: 56\nsupp see: 543\nmain see: 76\nok.') - - def test_dlfcn_basic(self): - return self.skip('shared libs are deprecated') - if Settings.ASM_JS: return self.skip('TODO: dlopen in asm') - - Settings.NAMED_GLOBALS = 1 - Settings.LINKABLE = 1 - - lib_src = ''' - #include - - class Foo { - public: - Foo() { - printf("Constructing lib object.\\n"); - } - }; - - Foo global; - ''' - dirname = self.get_dir() - filename = os.path.join(dirname, 'liblib.cpp') - Settings.BUILD_AS_SHARED_LIB = 1 - self.build(lib_src, dirname, filename) - shutil.move(filename + '.o.js', os.path.join(dirname, 'liblib.so')) - - src = ''' - #include - #include - - class Bar { - public: - Bar() { - printf("Constructing main object.\\n"); - } - }; - - Bar global; - - int main() { - dlopen("liblib.so", RTLD_NOW); - return 0; - } - ''' - Settings.BUILD_AS_SHARED_LIB = 0 - add_pre_run_and_checks = ''' -def process(filename): - src = open(filename, 'r').read().replace( - '// {{PRE_RUN_ADDITIONS}}', - "FS.createLazyFile('/', 'liblib.so', 'liblib.so', true, false);" - ) - open(filename, 'w').write(src) -''' - self.do_run(src, 'Constructing main object.\nConstructing lib object.\n', - post_build=add_pre_run_and_checks) - - def test_dlfcn_qsort(self): - return self.skip('shared libs are deprecated') - if self.emcc_args is None: return self.skip('requires emcc') - if Settings.ASM_JS: return self.skip('TODO: dlopen in asm') - - Settings.LINKABLE = 1 - Settings.NAMED_GLOBALS = 1 - - if Settings.USE_TYPED_ARRAYS == 2: - Settings.CORRECT_SIGNS = 1 # Needed for unsafe optimizations - - lib_src = ''' - int lib_cmp(const void* left, const void* right) { - const int* a = (const int*) left; - const int* b = (const int*) right; - if(*a > *b) return 1; - else if(*a == *b) return 0; - else return -1; - } - - typedef int (*CMP_TYPE)(const void*, const void*); - - extern "C" CMP_TYPE get_cmp() { - return lib_cmp; - } - ''' - dirname = self.get_dir() - filename = os.path.join(dirname, 'liblib.cpp') - Settings.BUILD_AS_SHARED_LIB = 1 - Settings.EXPORTED_FUNCTIONS = ['_get_cmp'] - self.build(lib_src, dirname, filename) - shutil.move(filename + '.o.js', os.path.join(dirname, 'liblib.so')) - - src = ''' - #include - #include - #include - - typedef int (*CMP_TYPE)(const void*, const void*); - - int main_cmp(const void* left, const void* right) { - const int* a = (const int*) left; - const int* b = (const int*) right; - if(*a < *b) return 1; - else if(*a == *b) return 0; - else return -1; - } - - int main() { - void* lib_handle; - CMP_TYPE (*getter_ptr)(); - CMP_TYPE lib_cmp_ptr; - int arr[5] = {4, 2, 5, 1, 3}; - - lib_handle = dlopen("liblib.so", RTLD_NOW); - if (lib_handle == NULL) { - printf("Could not load lib.\\n"); - return 1; - } - getter_ptr = (CMP_TYPE (*)()) dlsym(lib_handle, "get_cmp"); - if (getter_ptr == NULL) { - printf("Could not find func.\\n"); - return 1; - } - lib_cmp_ptr = getter_ptr(); - - qsort((void*)arr, 5, sizeof(int), main_cmp); - printf("Sort with main comparison: "); - for (int i = 0; i < 5; i++) { - printf("%d ", arr[i]); - } - printf("\\n"); - - qsort((void*)arr, 5, sizeof(int), lib_cmp_ptr); - printf("Sort with lib comparison: "); - for (int i = 0; i < 5; i++) { - printf("%d ", arr[i]); - } - printf("\\n"); - - return 0; - } - ''' - Settings.BUILD_AS_SHARED_LIB = 0 - Settings.EXPORTED_FUNCTIONS = ['_main'] - add_pre_run_and_checks = ''' -def process(filename): - src = open(filename, 'r').read().replace( - '// {{PRE_RUN_ADDITIONS}}', - "FS.createLazyFile('/', 'liblib.so', 'liblib.so', true, false);" - ) - open(filename, 'w').write(src) -''' - self.do_run(src, 'Sort with main comparison: 5 4 3 2 1 *Sort with lib comparison: 1 2 3 4 5 *', - output_nicerizer=lambda x, err: x.replace('\n', '*'), - post_build=add_pre_run_and_checks) - - def test_dlfcn_data_and_fptr(self): - return self.skip('shared libs are deprecated') - if Settings.ASM_JS: return self.skip('TODO: dlopen in asm') - if Building.LLVM_OPTS: return self.skip('LLVM opts will optimize out parent_func') - - Settings.LINKABLE = 1 - Settings.NAMED_GLOBALS = 1 - - lib_src = ''' - #include - - int global = 42; - - extern void parent_func(); // a function that is defined in the parent - - void lib_fptr() { - printf("Second calling lib_fptr from main.\\n"); - parent_func(); - // call it also through a pointer, to check indexizing - void (*p_f)(); - p_f = parent_func; - p_f(); - } - - extern "C" void (*func(int x, void(*fptr)()))() { - printf("In func: %d\\n", x); - fptr(); - return lib_fptr; - } - ''' - dirname = self.get_dir() - filename = os.path.join(dirname, 'liblib.cpp') - Settings.BUILD_AS_SHARED_LIB = 1 - Settings.EXPORTED_FUNCTIONS = ['_func'] - Settings.EXPORTED_GLOBALS = ['_global'] - self.build(lib_src, dirname, filename) - shutil.move(filename + '.o.js', os.path.join(dirname, 'liblib.so')) - - src = ''' - #include - #include - - typedef void (*FUNCTYPE(int, void(*)()))(); - - FUNCTYPE func; - - void parent_func() { - printf("parent_func called from child\\n"); - } - - void main_fptr() { - printf("First calling main_fptr from lib.\\n"); - } - - int main() { - void* lib_handle; - FUNCTYPE* func_fptr; - - // Test basic lib loading. - lib_handle = dlopen("liblib.so", RTLD_NOW); - if (lib_handle == NULL) { - printf("Could not load lib.\\n"); - return 1; - } - - // Test looked up function. - func_fptr = (FUNCTYPE*) dlsym(lib_handle, "func"); - // Load twice to test cache. - func_fptr = (FUNCTYPE*) dlsym(lib_handle, "func"); - if (func_fptr == NULL) { - printf("Could not find func.\\n"); - return 1; - } - - // Test passing function pointers across module bounds. - void (*fptr)() = func_fptr(13, main_fptr); - fptr(); - - // Test global data. - int* global = (int*) dlsym(lib_handle, "global"); - if (global == NULL) { - printf("Could not find global.\\n"); - return 1; - } - - printf("Var: %d\\n", *global); - - return 0; - } - ''' - Settings.BUILD_AS_SHARED_LIB = 0 - Settings.EXPORTED_FUNCTIONS = ['_main'] - Settings.EXPORTED_GLOBALS = [] - add_pre_run_and_checks = ''' -def process(filename): - src = open(filename, 'r').read().replace( - '// {{PRE_RUN_ADDITIONS}}', - "FS.createLazyFile('/', 'liblib.so', 'liblib.so', true, false);" - ) - open(filename, 'w').write(src) -''' - self.do_run(src, 'In func: 13*First calling main_fptr from lib.*Second calling lib_fptr from main.*parent_func called from child*parent_func called from child*Var: 42*', - output_nicerizer=lambda x, err: x.replace('\n', '*'), - post_build=add_pre_run_and_checks) - - def test_dlfcn_alias(self): - return self.skip('shared libs are deprecated') - if Settings.ASM_JS: return self.skip('TODO: dlopen in asm') - - Settings.LINKABLE = 1 - Settings.NAMED_GLOBALS = 1 - - if Building.LLVM_OPTS == 2: return self.skip('LLVM LTO will optimize away stuff we expect from the shared library') - - lib_src = r''' - #include - extern int parent_global; - extern "C" void func() { - printf("Parent global: %d.\n", parent_global); - } - ''' - dirname = self.get_dir() - filename = os.path.join(dirname, 'liblib.cpp') - Settings.BUILD_AS_SHARED_LIB = 1 - Settings.EXPORTED_FUNCTIONS = ['_func'] - self.build(lib_src, dirname, filename) - shutil.move(filename + '.o.js', os.path.join(dirname, 'liblib.so')) - - src = r''' - #include - - int parent_global = 123; - - int main() { - void* lib_handle; - void (*fptr)(); - - lib_handle = dlopen("liblib.so", RTLD_NOW); - fptr = (void (*)())dlsym(lib_handle, "func"); - fptr(); - parent_global = 456; - fptr(); - - return 0; - } - ''' - Settings.BUILD_AS_SHARED_LIB = 0 - Settings.INCLUDE_FULL_LIBRARY = 1 - Settings.EXPORTED_FUNCTIONS = ['_main'] - add_pre_run_and_checks = ''' -def process(filename): - src = open(filename, 'r').read().replace( - '// {{PRE_RUN_ADDITIONS}}', - "FS.createLazyFile('/', 'liblib.so', 'liblib.so', true, false);" - ) - open(filename, 'w').write(src) -''' - self.do_run(src, 'Parent global: 123.*Parent global: 456.*', - output_nicerizer=lambda x, err: x.replace('\n', '*'), - post_build=add_pre_run_and_checks, - extra_emscripten_args=['-H', 'libc/fcntl.h,libc/sys/unistd.h,poll.h,libc/math.h,libc/time.h,libc/langinfo.h']) - Settings.INCLUDE_FULL_LIBRARY = 0 - - def test_dlfcn_varargs(self): - return self.skip('shared libs are deprecated') - if Settings.ASM_JS: return self.skip('TODO: dlopen in asm') - - Settings.LINKABLE = 1 - Settings.NAMED_GLOBALS = 1 - - if Building.LLVM_OPTS == 2: return self.skip('LLVM LTO will optimize things that prevent shared objects from working') - if Settings.QUANTUM_SIZE == 1: return self.skip('FIXME: Add support for this') - - lib_src = r''' - void print_ints(int n, ...); - extern "C" void func() { - print_ints(2, 13, 42); - } - ''' - dirname = self.get_dir() - filename = os.path.join(dirname, 'liblib.cpp') - Settings.BUILD_AS_SHARED_LIB = 1 - Settings.EXPORTED_FUNCTIONS = ['_func'] - self.build(lib_src, dirname, filename) - shutil.move(filename + '.o.js', os.path.join(dirname, 'liblib.so')) - - src = r''' - #include - #include - #include - - void print_ints(int n, ...) { - va_list args; - va_start(args, n); - for (int i = 0; i < n; i++) { - printf("%d\n", va_arg(args, int)); - } - va_end(args); - } - - int main() { - void* lib_handle; - void (*fptr)(); - - print_ints(2, 100, 200); - - lib_handle = dlopen("liblib.so", RTLD_NOW); - fptr = (void (*)())dlsym(lib_handle, "func"); - fptr(); - - return 0; - } - ''' - Settings.BUILD_AS_SHARED_LIB = 0 - Settings.EXPORTED_FUNCTIONS = ['_main'] - add_pre_run_and_checks = ''' -def process(filename): - src = open(filename, 'r').read().replace( - '// {{PRE_RUN_ADDITIONS}}', - "FS.createLazyFile('/', 'liblib.so', 'liblib.so', true, false);" - ) - open(filename, 'w').write(src) -''' - self.do_run(src, '100\n200\n13\n42\n', - post_build=add_pre_run_and_checks) - - def test_dlfcn_self(self): - if Settings.USE_TYPED_ARRAYS == 1: return self.skip('Does not work with USE_TYPED_ARRAYS=1') - Settings.DLOPEN_SUPPORT = 1 - - src = r''' -#include -#include - -int global = 123; - -extern "C" __attribute__((noinline)) void foo(int x) { - printf("%d\n", x); -} - -extern "C" __attribute__((noinline)) void repeatable() { - void* self = dlopen(NULL, RTLD_LAZY); - int* global_ptr = (int*)dlsym(self, "global"); - void (*foo_ptr)(int) = (void (*)(int))dlsym(self, "foo"); - foo_ptr(*global_ptr); - dlclose(self); -} - -int main() { - repeatable(); - repeatable(); - return 0; -}''' - def post(filename): - with open(filename) as f: - for line in f: - if 'var SYMBOL_TABLE' in line: - table = line - break - else: - raise Exception('Could not find symbol table!') - import json - table = json.loads(table[table.find('{'):table.rfind('}')+1]) - actual = list(sorted(table.keys())) - # ensure there aren't too many globals; we don't want unnamed_addr - assert actual == ['_foo', '_global', '_main', '_repeatable'], \ - "Symbol table does not match: %s" % actual - - self.do_run(src, '123\n123', post_build=(None, post)) - - def test_rand(self): - return self.skip('rand() is now random') # FIXME - - src = r''' - #include - #include - - int main() { - printf("%d\n", rand()); - printf("%d\n", rand()); - - srand(123); - printf("%d\n", rand()); - printf("%d\n", rand()); - srand(123); - printf("%d\n", rand()); - printf("%d\n", rand()); - - unsigned state = 0; - int r; - r = rand_r(&state); - printf("%d, %u\n", r, state); - r = rand_r(&state); - printf("%d, %u\n", r, state); - state = 0; - r = rand_r(&state); - printf("%d, %u\n", r, state); - - return 0; - } - ''' - expected = ''' - 1250496027 - 1116302336 - 440917656 - 1476150784 - 440917656 - 1476150784 - 12345, 12345 - 1406932606, 3554416254 - 12345, 12345 - ''' - self.do_run(src, re.sub(r'(^|\n)\s+', r'\1', expected)) - - def test_strtod(self): - if self.emcc_args is None: return self.skip('needs emcc for libc') - - src = r''' - #include - #include - - int main() { - char* endptr; - - printf("\n"); - printf("%g\n", strtod("0", &endptr)); - printf("%g\n", strtod("0.", &endptr)); - printf("%g\n", strtod("0.0", &endptr)); - printf("%g\n", strtod("-0.0", &endptr)); - printf("%g\n", strtod("1", &endptr)); - printf("%g\n", strtod("1.", &endptr)); - printf("%g\n", strtod("1.0", &endptr)); - printf("%g\n", strtod("z1.0", &endptr)); - printf("%g\n", strtod("0.5", &endptr)); - printf("%g\n", strtod(".5", &endptr)); - printf("%g\n", strtod(".a5", &endptr)); - printf("%g\n", strtod("123", &endptr)); - printf("%g\n", strtod("123.456", &endptr)); - printf("%g\n", strtod("-123.456", &endptr)); - printf("%g\n", strtod("1234567891234567890", &endptr)); - printf("%g\n", strtod("1234567891234567890e+50", &endptr)); - printf("%g\n", strtod("84e+220", &endptr)); - printf("%g\n", strtod("123e-50", &endptr)); - printf("%g\n", strtod("123e-250", &endptr)); - printf("%g\n", strtod("123e-450", &endptr)); - - char str[] = " 12.34e56end"; - printf("%g\n", strtod(str, &endptr)); - printf("%d\n", endptr - str); - printf("%g\n", strtod("84e+420", &endptr)); - - printf("%.12f\n", strtod("1.2345678900000000e+08", NULL)); - - return 0; - } - ''' - expected = ''' - 0 - 0 - 0 - -0 - 1 - 1 - 1 - 0 - 0.5 - 0.5 - 0 - 123 - 123.456 - -123.456 - 1.23457e+18 - 1.23457e+68 - 8.4e+221 - 1.23e-48 - 1.23e-248 - 0 - 1.234e+57 - 10 - inf - 123456789.000000000000 - ''' - - self.do_run(src, re.sub(r'\n\s+', '\n', expected)) - self.do_run(src.replace('strtod', 'strtold'), re.sub(r'\n\s+', '\n', expected)) # XXX add real support for long double - - def test_strtok(self): - src = r''' - #include - #include - - int main() { - char test[80], blah[80]; - char *sep = "\\/:;=-"; - char *word, *phrase, *brkt, *brkb; - - strcpy(test, "This;is.a:test:of=the/string\\tokenizer-function."); - - for (word = strtok_r(test, sep, &brkt); word; word = strtok_r(NULL, sep, &brkt)) { - strcpy(blah, "blah:blat:blab:blag"); - for (phrase = strtok_r(blah, sep, &brkb); phrase; phrase = strtok_r(NULL, sep, &brkb)) { - printf("at %s:%s\n", word, phrase); - } - } - return 0; - } - ''' - - expected = '''at This:blah -at This:blat -at This:blab -at This:blag -at is.a:blah -at is.a:blat -at is.a:blab -at is.a:blag -at test:blah -at test:blat -at test:blab -at test:blag -at of:blah -at of:blat -at of:blab -at of:blag -at the:blah -at the:blat -at the:blab -at the:blag -at string:blah -at string:blat -at string:blab -at string:blag -at tokenizer:blah -at tokenizer:blat -at tokenizer:blab -at tokenizer:blag -at function.:blah -at function.:blat -at function.:blab -at function.:blag -''' - self.do_run(src, expected) - - def test_parseInt(self): - if Settings.USE_TYPED_ARRAYS != 2: return self.skip('i64 mode 1 requires ta2') - if Settings.QUANTUM_SIZE == 1: return self.skip('Q1 and I64_1 do not mix well yet') - src = open(path_from_root('tests', 'parseInt', 'src.c'), 'r').read() - expected = open(path_from_root('tests', 'parseInt', 'output.txt'), 'r').read() - self.do_run(src, expected) - - def test_transtrcase(self): - src = ''' - #include - #include - int main() { - char szToupr[] = "hello, "; - char szTolwr[] = "EMSCRIPTEN"; - strupr(szToupr); - strlwr(szTolwr); - printf(szToupr); - printf(szTolwr); - return 0; - } - ''' - self.do_run(src, 'HELLO, emscripten') - - def test_printf(self): - if Settings.USE_TYPED_ARRAYS != 2: return self.skip('i64 mode 1 requires ta2') - self.banned_js_engines = [NODE_JS, V8_ENGINE] # SpiderMonkey and V8 do different things to float64 typed arrays, un-NaNing, etc. - src = open(path_from_root('tests', 'printf', 'test.c'), 'r').read() - expected = [open(path_from_root('tests', 'printf', 'output.txt'), 'r').read(), - open(path_from_root('tests', 'printf', 'output_i64_1.txt'), 'r').read()] - self.do_run(src, expected) - - def test_printf_2(self): - src = r''' - #include - - int main() { - char c = '1'; - short s = 2; - int i = 3; - long long l = 4; - float f = 5.5; - double d = 6.6; - - printf("%c,%hd,%d,%lld,%.1f,%.1llf\n", c, s, i, l, f, d); - printf("%#x,%#x\n", 1, 0); - - return 0; - } - ''' - self.do_run(src, '1,2,3,4,5.5,6.6\n0x1,0\n') - - def test_vprintf(self): - src = r''' - #include - #include - - void print(char* format, ...) { - va_list args; - va_start (args, format); - vprintf (format, args); - va_end (args); - } - - int main () { - print("Call with %d variable argument.\n", 1); - print("Call with %d variable %s.\n", 2, "arguments"); - - return 0; - } - ''' - expected = ''' - Call with 1 variable argument. - Call with 2 variable arguments. - ''' - self.do_run(src, re.sub('(^|\n)\s+', '\\1', expected)) - - def test_vsnprintf(self): - if self.emcc_args is None: return self.skip('needs i64 math') - - src = r''' - #include - #include - #include - - void printy(const char *f, ...) - { - char buffer[256]; - va_list args; - va_start(args, f); - vsnprintf(buffer, 256, f, args); - puts(buffer); - va_end(args); - } - - int main(int argc, char **argv) { - int64_t x = argc - 1; - int64_t y = argc - 1 + 0x400000; - if (x % 3 == 2) y *= 2; - - printy("0x%llx_0x%llx", x, y); - printy("0x%llx_0x%llx", x, x); - printy("0x%llx_0x%llx", y, x); - printy("0x%llx_0x%llx", y, y); - - { - uint64_t A = 0x800000; - uint64_t B = 0x800000000000ULL; - printy("0x%llx_0x%llx", A, B); - } - { - uint64_t A = 0x800; - uint64_t B = 0x12340000000000ULL; - printy("0x%llx_0x%llx", A, B); - } - { - uint64_t A = 0x000009182746756; - uint64_t B = 0x192837465631ACBDULL; - printy("0x%llx_0x%llx", A, B); - } - - return 0; - } - ''' - self.do_run(src, '''0x0_0x400000 -0x0_0x0 -0x400000_0x0 -0x400000_0x400000 -0x800000_0x800000000000 -0x800_0x12340000000000 -0x9182746756_0x192837465631acbd -''') - - def test_printf_more(self): - src = r''' - #include - int main() { - int size = snprintf(NULL, 0, "%s %d %.2f\n", "me and myself", 25, 1.345); - char buf[size]; - snprintf(buf, size, "%s %d %.2f\n", "me and myself", 25, 1.345); - printf("%d : %s\n", size, buf); - char *buff = NULL; - asprintf(&buff, "%d waka %d\n", 21, 95); - puts(buff); - return 0; - } - ''' - self.do_run(src, '22 : me and myself 25 1.34\n21 waka 95\n') - - def test_perrar(self): - src = r''' - #include - #include - #include - #include - - int main( int argc, char** argv ){ - int retval = open( "NonExistingFile", O_RDONLY ); - if( retval == -1 ) - perror( "Cannot open NonExistingFile" ); - return 0; - } - ''' - self.do_run(src, 'Cannot open NonExistingFile: No such file or directory\n') - - def test_atoX(self): - if self.emcc_args is None: return self.skip('requires ta2') - - src = r''' - #include - #include - - int main () { - printf("%d*", atoi("")); - printf("%d*", atoi("a")); - printf("%d*", atoi(" b")); - printf("%d*", atoi(" c ")); - printf("%d*", atoi("6")); - printf("%d*", atoi(" 5")); - printf("%d*", atoi("4 ")); - printf("%d*", atoi("3 6")); - printf("%d*", atoi(" 3 7")); - printf("%d*", atoi("9 d")); - printf("%d\n", atoi(" 8 e")); - printf("%d*", atol("")); - printf("%d*", atol("a")); - printf("%d*", atol(" b")); - printf("%d*", atol(" c ")); - printf("%d*", atol("6")); - printf("%d*", atol(" 5")); - printf("%d*", atol("4 ")); - printf("%d*", atol("3 6")); - printf("%d*", atol(" 3 7")); - printf("%d*", atol("9 d")); - printf("%d\n", atol(" 8 e")); - printf("%lld*", atoll("6294967296")); - printf("%lld*", atoll("")); - printf("%lld*", atoll("a")); - printf("%lld*", atoll(" b")); - printf("%lld*", atoll(" c ")); - printf("%lld*", atoll("6")); - printf("%lld*", atoll(" 5")); - printf("%lld*", atoll("4 ")); - printf("%lld*", atoll("3 6")); - printf("%lld*", atoll(" 3 7")); - printf("%lld*", atoll("9 d")); - printf("%lld\n", atoll(" 8 e")); - return 0; - } - ''' - self.do_run(src, '0*0*0*0*6*5*4*3*3*9*8\n0*0*0*0*6*5*4*3*3*9*8\n6294967296*0*0*0*0*6*5*4*3*3*9*8\n') - - def test_strstr(self): - src = r''' - #include - #include - - int main() - { - printf("%d\n", !!strstr("\\n", "\\n")); - printf("%d\n", !!strstr("cheezy", "ez")); - printf("%d\n", !!strstr("cheeezy", "ez")); - printf("%d\n", !!strstr("cheeeeeeeeeezy", "ez")); - printf("%d\n", !!strstr("cheeeeeeeeee1zy", "ez")); - printf("%d\n", !!strstr("che1ezy", "ez")); - printf("%d\n", !!strstr("che1ezy", "che")); - printf("%d\n", !!strstr("ce1ezy", "che")); - printf("%d\n", !!strstr("ce1ezy", "ezy")); - printf("%d\n", !!strstr("ce1ezyt", "ezy")); - printf("%d\n", !!strstr("ce1ez1y", "ezy")); - printf("%d\n", !!strstr("cheezy", "a")); - printf("%d\n", !!strstr("cheezy", "b")); - printf("%d\n", !!strstr("cheezy", "c")); - printf("%d\n", !!strstr("cheezy", "d")); - printf("%d\n", !!strstr("cheezy", "g")); - printf("%d\n", !!strstr("cheezy", "h")); - printf("%d\n", !!strstr("cheezy", "i")); - printf("%d\n", !!strstr("cheezy", "e")); - printf("%d\n", !!strstr("cheezy", "x")); - printf("%d\n", !!strstr("cheezy", "y")); - printf("%d\n", !!strstr("cheezy", "z")); - printf("%d\n", !!strstr("cheezy", "_")); - - const char *str = "a big string"; - printf("%d\n", strstr(str, "big") - str); - return 0; - } - ''' - self.do_run(src, '''1 -1 -1 -1 -0 -1 -1 -0 -1 -1 -0 -0 -0 -1 -0 -0 -1 -0 -1 -0 -1 -1 -0 -2 -''') - - def test_sscanf(self): - if self.emcc_args is None: return self.skip('needs emcc for libc') - - src = r''' - #include - #include - #include - - int main () { - #define CHECK(str) \ - { \ - char name[1000]; \ - memset(name, 0, 1000); \ - int prio = 99; \ - sscanf(str, "%s %d", name, &prio); \ - printf("%s : %d\n", name, prio); \ - } - CHECK("en-us 2"); - CHECK("en-r"); - CHECK("en 3"); - - printf("%f, %f\n", atof("1.234567"), atof("cheez")); - - char float_formats[] = "fegE"; - char format[] = "%_"; - for(int i = 0; i < 4; ++i) { - format[1] = float_formats[i]; - - float n = -1; - sscanf(" 2.8208", format, &n); - printf("%.4f\n", n); - - float a = -1; - sscanf("-3.03", format, &a); - printf("%.4f\n", a); - } - - char buffy[100]; - sscanf("cheez some thing moar 123\nyet more\n", "cheez %s", buffy); - printf("|%s|\n", buffy); - sscanf("cheez something\nmoar 123\nyet more\n", "cheez %s", buffy); - printf("|%s|\n", buffy); - sscanf("cheez somethingmoar\tyet more\n", "cheez %s", buffy); - printf("|%s|\n", buffy); - - int numverts = -1; - printf("%d\n", sscanf(" numverts 1499\n", " numverts %d", &numverts)); // white space is the same, even if tab vs space - printf("%d\n", numverts); - - int index; - float u, v; - short start, count; - printf("%d\n", sscanf(" vert 87 ( 0.481565 0.059481 ) 0 1\n", " vert %d ( %f %f ) %hu %hu", &index, &u, &v, &start, &count)); - printf("%d,%.6f,%.6f,%hu,%hu\n", index, u, v, start, count); - - int neg, neg2, neg3 = 0; - printf("%d\n", sscanf("-123 -765 -34-6", "%d %u %d", &neg, &neg2, &neg3)); - printf("%d,%u,%d\n", neg, neg2, neg3); - - { - int a = 0; - sscanf("1", "%i", &a); - printf("%i\n", a); - } - - return 0; - } - ''' - self.do_run(src, 'en-us : 2\nen-r : 99\nen : 3\n1.234567, 0.000000\n2.8208\n-3.0300\n2.8208\n-3.0300\n2.8208\n-3.0300\n2.8208\n-3.0300\n|some|\n|something|\n|somethingmoar|\n' + - '1\n1499\n' + - '5\n87,0.481565,0.059481,0,1\n' + - '3\n-123,4294966531,-34\n' + - '1\n') - - def test_sscanf_2(self): - # doubles - if Settings.USE_TYPED_ARRAYS == 2: - for ftype in ['float', 'double']: - src = r''' - #include - - int main(){ - char strval1[] = "1.2345678901"; - char strval2[] = "1.23456789e5"; - char strval3[] = "1.23456789E5"; - char strval4[] = "1.2345678e-5"; - char strval5[] = "1.2345678E-5"; - double dblval = 1.2345678901; - double tstval; - - sscanf(strval1, "%lf", &tstval); - if(dblval != tstval) printf("FAIL: Values are not equal: %lf %lf\n", dblval, tstval); - else printf("Pass: %lf %lf\n", tstval, dblval); - - sscanf(strval2, "%lf", &tstval); - dblval = 123456.789; - if(dblval != tstval) printf("FAIL: Values are not equal: %lf %lf\n", dblval, tstval); - else printf("Pass: %lf %lf\n", tstval, dblval); - - sscanf(strval3, "%lf", &tstval); - dblval = 123456.789; - if(dblval != tstval) printf("FAIL: Values are not equal: %lf %lf\n", dblval, tstval); - else printf("Pass: %lf %lf\n", tstval, dblval); - - sscanf(strval4, "%lf", &tstval); - dblval = 0.000012345678; - if(dblval != tstval) printf("FAIL: Values are not equal: %lf %lf\n", dblval, tstval); - else printf("Pass: %lf %lf\n", tstval, dblval); - - sscanf(strval5, "%lf", &tstval); - dblval = 0.000012345678; - if(dblval != tstval) printf("FAIL: Values are not equal: %lf %lf\n", dblval, tstval); - else printf("Pass: %lf %lf\n", tstval, dblval); - - return 0; - } - ''' - if ftype == 'float': - self.do_run(src.replace('%lf', '%f').replace('double', 'float'), '''Pass: 1.234568 1.234568 -Pass: 123456.789063 123456.789063 -Pass: 123456.789063 123456.789063 -Pass: 0.000012 0.000012 -Pass: 0.000012 0.000012''') - else: - self.do_run(src, '''Pass: 1.234568 1.234568 -Pass: 123456.789000 123456.789000 -Pass: 123456.789000 123456.789000 -Pass: 0.000012 0.000012 -Pass: 0.000012 0.000012''') - - def test_sscanf_n(self): - src = r''' - #include - int main() { - char *line = "version 1.0"; - int i, l, lineno; - char word[80]; - if (sscanf(line, "%s%n", word, &l) != 1) { - printf("Header format error, line %d\n", lineno); - } - printf("[DEBUG] word 1: %s, l: %d\n", word, l); - - int x = sscanf("one %n two", "%s %n", word, &l); - printf("%d,%s,%d\n", x, word, l); - { - int a, b, c, count; - count = sscanf("12345 6789", "%d %n%d", &a, &b, &c); - printf("%i %i %i %i\n", count, a, b, c); - } - return 0; - } - ''' - self.do_run(src, '''[DEBUG] word 1: version, l: 7\n1,one,4\n2 12345 6 6789\n''') - - def test_sscanf_whitespace(self): - src = r''' - #include - - int main() { - short int x; - short int y; - - const char* buffer[] = { - "173,16", - " 16,173", - "183, 173", - " 17, 287", - " 98, 123, " - }; - - for (int i=0; i<5; ++i) { - sscanf(buffer[i], "%hd,%hd", &x, &y); - printf("%d:%d,%d ", i, x, y); - } - - return 0; - } - ''' - self.do_run(src, '''0:173,16 1:16,173 2:183,173 3:17,287 4:98,123''') - - def test_sscanf_other_whitespace(self): - Settings.SAFE_HEAP = 0 # use i16s in printf - - src = r''' - #include - - int main() { - short int x; - short int y; - - const char* buffer[] = { - "\t2\t3\t", /* TAB - horizontal tab */ - "\t\t5\t\t7\t\t", - "\n11\n13\n", /* LF - line feed */ - "\n\n17\n\n19\n\n", - "\v23\v29\v", /* VT - vertical tab */ - "\v\v31\v\v37\v\v", - "\f41\f43\f", /* FF - form feed */ - "\f\f47\f\f53\f\f", - "\r59\r61\r", /* CR - carrage return */ - "\r\r67\r\r71\r\r" - }; - - for (int i=0; i<10; ++i) { - x = 0; y = 0; - sscanf(buffer[i], " %d %d ", &x, &y); - printf("%d, %d, ", x, y); - } - - return 0; - } - ''' - self.do_run(src, '''2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, ''') - - def test_sscanf_3(self): - # i64 - if not Settings.USE_TYPED_ARRAYS == 2: return self.skip('64-bit sscanf only supported in ta2') - src = r''' - #include - #include - - int main(){ - - int64_t s, m, l; - printf("%d\n", sscanf("123 1073741823 1125899906842620", "%lld %lld %lld", &s, &m, &l)); - printf("%lld,%lld,%lld\n", s, m, l); - - int64_t negS, negM, negL; - printf("%d\n", sscanf("-123 -1073741823 -1125899906842620", "%lld %lld %lld", &negS, &negM, &negL)); - printf("%lld,%lld,%lld\n", negS, negM, negL); - - return 0; - } - ''' - - self.do_run(src, '3\n123,1073741823,1125899906842620\n' + - '3\n-123,-1073741823,-1125899906842620\n') - - def test_sscanf_4(self): - src = r''' - #include - - int main() - { - char pYear[16], pMonth[16], pDay[16], pDate[64]; - printf("%d\n", sscanf("Nov 19 2012", "%s%s%s", pMonth, pDay, pYear)); - printf("day %s, month %s, year %s \n", pDay, pMonth, pYear); - return(0); - } - ''' - self.do_run(src, '3\nday 19, month Nov, year 2012'); - - def test_sscanf_5(self): - src = r''' - #include "stdio.h" - - static const char *colors[] = { - " c black", - ". c #001100", - "X c #111100" - }; - - int main(){ - unsigned char code; - char color[32]; - int rcode; - for(int i = 0; i < 3; i++) { - rcode = sscanf(colors[i], "%c c %s", &code, color); - printf("%i, %c, %s\n", rcode, code, color); - } - } - ''' - self.do_run(src, '2, , black\n2, ., #001100\n2, X, #111100'); - - def test_sscanf_6(self): - src = r''' - #include - #include - int main() - { - char *date = "18.07.2013w"; - char c[10]; - memset(c, 0, 10); - int y, m, d, i; - i = sscanf(date, "%d.%d.%4d%c", &d, &m, &y, c); - printf("date: %s; day %2d, month %2d, year %4d, extra: %c, %d\n", date, d, m, y, c[0], i); - i = sscanf(date, "%d.%d.%3c", &d, &m, c); - printf("date: %s; day %2d, month %2d, year %4d, extra: %s, %d\n", date, d, m, y, c, i); - } - ''' - self.do_run(src, '''date: 18.07.2013w; day 18, month 7, year 2013, extra: w, 4 -date: 18.07.2013w; day 18, month 7, year 2013, extra: 201, 3 -'''); - - def test_sscanf_skip(self): - if Settings.USE_TYPED_ARRAYS != 2: return self.skip("need ta2 for full i64") - - src = r''' - #include - #include - - int main(){ - int val1; - printf("%d\n", sscanf("10 20 30 40", "%*lld %*d %d", &val1)); - printf("%d\n", val1); - - int64_t large, val2; - printf("%d\n", sscanf("1000000 -1125899906842620 -123 -1073741823", "%lld %*lld %ld %*d", &large, &val2)); - printf("%lld,%d\n", large, val2); - - return 0; - } - ''' - self.do_run(src, '1\n30\n2\n1000000,-123\n') - - def test_sscanf_caps(self): - src = r''' - #include "stdio.h" - - int main(){ - unsigned int a; - float e, f, g; - sscanf("a 1.1 1.1 1.1", "%X %E %F %G", &a, &e, &f, &g); - printf("%d %.1F %.1F %.1F\n", a, e, f, g); - } - ''' - self.do_run(src, '10 1.1 1.1 1.1'); - - def test_langinfo(self): - src = open(path_from_root('tests', 'langinfo', 'test.c'), 'r').read() - expected = open(path_from_root('tests', 'langinfo', 'output.txt'), 'r').read() - self.do_run(src, expected, extra_emscripten_args=['-H', 'libc/langinfo.h']) - - def test_files(self): - if self.emcc_args is not None and '-O2' in self.emcc_args: - self.emcc_args += ['--closure', '1'] # Use closure here, to test we don't break FS stuff - - Settings.CORRECT_SIGNS = 1 # Just so our output is what we expect. Can flip them both. - post = ''' -def process(filename): - src = \'\'\' - var Module = { - 'noFSInit': true, - 'preRun': function() { - FS.createLazyFile('/', 'test.file', 'test.file', true, false); - // Test FS_* exporting - Module['FS_createDataFile']('/', 'somefile.binary', [100, 200, 50, 25, 10, 77, 123], true, false); // 200 becomes -56, since signed chars are used in memory - var test_files_input = 'hi there!'; - var test_files_input_index = 0; - FS.init(function() { - return test_files_input.charCodeAt(test_files_input_index++) || null; - }); - } - }; - \'\'\' + open(filename, 'r').read() - open(filename, 'w').write(src) -''' - other = open(os.path.join(self.get_dir(), 'test.file'), 'w') - other.write('some data'); - other.close() - - src = open(path_from_root('tests', 'files.cpp'), 'r').read() - self.do_run(src, ('size: 7\ndata: 100,-56,50,25,10,77,123\nloop: 100 -56 50 25 10 77 123 \ninput:hi there!\ntexto\n$\n5 : 10,30,20,11,88\nother=some data.\nseeked=me da.\nseeked=ata.\nseeked=ta.\nfscanfed: 10 - hello\nok.\ntexte\n', 'size: 7\ndata: 100,-56,50,25,10,77,123\nloop: 100 -56 50 25 10 77 123 \ninput:hi there!\ntexto\ntexte\n$\n5 : 10,30,20,11,88\nother=some data.\nseeked=me da.\nseeked=ata.\nseeked=ta.\nfscanfed: 10 - hello\nok.\n'), - post_build=post, extra_emscripten_args=['-H', 'libc/fcntl.h']) - - def test_files_m(self): - # Test for Module.stdin etc. - - Settings.CORRECT_SIGNS = 1 - - post = ''' -def process(filename): - src = \'\'\' - var data = [10, 20, 40, 30]; - var Module = { - stdin: function() { return data.pop() || null }, - stdout: function(x) { Module.print('got: ' + x) } - }; - \'\'\' + open(filename, 'r').read() - open(filename, 'w').write(src) -''' - src = r''' - #include - #include - - int main () { - char c; - fprintf(stderr, "isatty? %d,%d,%d\n", isatty(fileno(stdin)), isatty(fileno(stdout)), isatty(fileno(stderr))); - while ((c = fgetc(stdin)) != EOF) { - putc(c+5, stdout); - } - return 0; - } - ''' - self.do_run(src, ('got: 35\ngot: 45\ngot: 25\ngot: 15\nisatty? 0,0,1\n', 'isatty? 0,0,1\ngot: 35\ngot: 45\ngot: 25\ngot: 15\n'), post_build=post) - - def test_fwrite_0(self): - src = r''' - #include - #include - - int main () - { - FILE *fh; - - fh = fopen("a.txt", "wb"); - if (!fh) exit(1); - fclose(fh); - - fh = fopen("a.txt", "rb"); - if (!fh) exit(1); - - char data[] = "foobar"; - size_t written = fwrite(data, 1, sizeof(data), fh); - - printf("written=%zu\n", written); - } - ''' - self.do_run(src, 'written=0') - - def test_fgetc_ungetc(self): - src = open(path_from_root('tests', 'stdio', 'test_fgetc_ungetc.c'), 'r').read() - self.do_run(src, 'success', force_c=True) - - def test_fgetc_unsigned(self): - if self.emcc_args is None: return self.skip('requires emcc') - src = r''' - #include - int main() { - FILE *file = fopen("file_with_byte_234.txt", "rb"); - int c = fgetc(file); - printf("*%d\n", c); - } - ''' - open('file_with_byte_234.txt', 'wb').write('\xea') - self.emcc_args += ['--embed-file', 'file_with_byte_234.txt'] - self.do_run(src, '*234\n') - - def test_fgets_eol(self): - if self.emcc_args is None: return self.skip('requires emcc') - src = r''' - #include - char buf[32]; - int main() - { - char *r = "SUCCESS"; - FILE *f = fopen("eol.txt", "r"); - while (fgets(buf, 32, f) != NULL) { - if (buf[0] == '\0') { - r = "FAIL"; - break; - } - } - printf("%s\n", r); - fclose(f); - return 0; - } - ''' - open('eol.txt', 'wb').write('\n') - self.emcc_args += ['--embed-file', 'eol.txt'] - self.do_run(src, 'SUCCESS\n') - - def test_fscanf(self): - if self.emcc_args is None: return self.skip('requires emcc') - open(os.path.join(self.get_dir(), 'three_numbers.txt'), 'w').write('''-1 0.1 -.1''') - src = r''' - #include - #include - #include - int main() - { - float x = FLT_MAX, y = FLT_MAX, z = FLT_MAX; - - FILE* fp = fopen("three_numbers.txt", "r"); - if (fp) { - int match = fscanf(fp, " %f %f %f ", &x, &y, &z); - printf("match = %d\n", match); - printf("x = %0.1f, y = %0.1f, z = %0.1f\n", x, y, z); - } else { - printf("failed to open three_numbers.txt\n"); - } - return 0; - } - ''' - self.emcc_args += ['--embed-file', 'three_numbers.txt'] - self.do_run(src, 'match = 3\nx = -1.0, y = 0.1, z = -0.1\n') - - def test_readdir(self): - src = open(path_from_root('tests', 'dirent', 'test_readdir.c'), 'r').read() - self.do_run(src, 'success', force_c=True) - - def test_stat(self): - src = open(path_from_root('tests', 'stat', 'test_stat.c'), 'r').read() - self.do_run(src, 'success', force_c=True) - - def test_stat_chmod(self): - src = open(path_from_root('tests', 'stat', 'test_chmod.c'), 'r').read() - self.do_run(src, 'success', force_c=True) - - def test_stat_mknod(self): - src = open(path_from_root('tests', 'stat', 'test_mknod.c'), 'r').read() - self.do_run(src, 'success', force_c=True) - - def test_fcntl(self): - add_pre_run = ''' -def process(filename): - src = open(filename, 'r').read().replace( - '// {{PRE_RUN_ADDITIONS}}', - "FS.createDataFile('/', 'test', 'abcdef', true, true);" - ) - open(filename, 'w').write(src) -''' - src = open(path_from_root('tests', 'fcntl', 'src.c'), 'r').read() - expected = open(path_from_root('tests', 'fcntl', 'output.txt'), 'r').read() - self.do_run(src, expected, post_build=add_pre_run, extra_emscripten_args=['-H', 'libc/fcntl.h']) - - def test_fcntl_open(self): - src = open(path_from_root('tests', 'fcntl-open', 'src.c'), 'r').read() - expected = open(path_from_root('tests', 'fcntl-open', 'output.txt'), 'r').read() - self.do_run(src, expected, force_c=True, extra_emscripten_args=['-H', 'libc/fcntl.h']) - - def test_fcntl_misc(self): - add_pre_run = ''' -def process(filename): - src = open(filename, 'r').read().replace( - '// {{PRE_RUN_ADDITIONS}}', - "FS.createDataFile('/', 'test', 'abcdef', true, true);" - ) - open(filename, 'w').write(src) -''' - src = open(path_from_root('tests', 'fcntl-misc', 'src.c'), 'r').read() - expected = open(path_from_root('tests', 'fcntl-misc', 'output.txt'), 'r').read() - self.do_run(src, expected, post_build=add_pre_run, extra_emscripten_args=['-H', 'libc/fcntl.h']) - - def test_poll(self): - add_pre_run = ''' -def process(filename): - src = open(filename, 'r').read().replace( - '// {{PRE_RUN_ADDITIONS}}', - \'\'\' - FS.createDataFile('/', 'file', 'abcdef', true, true); - FS.createDevice('/', 'device', function() {}, function() {}); - \'\'\' - ) - open(filename, 'w').write(src) -''' - src = r''' - #include - #include - #include - #include - - int main() { - struct pollfd multi[5]; - multi[0].fd = open("/file", O_RDONLY, 0777); - multi[1].fd = open("/device", O_RDONLY, 0777); - multi[2].fd = 123; - multi[3].fd = open("/file", O_RDONLY, 0777); - multi[4].fd = open("/file", O_RDONLY, 0777); - multi[0].events = POLLIN | POLLOUT | POLLNVAL | POLLERR; - multi[1].events = POLLIN | POLLOUT | POLLNVAL | POLLERR; - multi[2].events = POLLIN | POLLOUT | POLLNVAL | POLLERR; - multi[3].events = 0x00; - multi[4].events = POLLOUT | POLLNVAL | POLLERR; - - printf("ret: %d\n", poll(multi, 5, 123)); - printf("errno: %d\n", errno); - printf("multi[0].revents: %d\n", multi[0].revents == (POLLIN | POLLOUT)); - printf("multi[1].revents: %d\n", multi[1].revents == (POLLIN | POLLOUT)); - printf("multi[2].revents: %d\n", multi[2].revents == POLLNVAL); - printf("multi[3].revents: %d\n", multi[3].revents == 0); - printf("multi[4].revents: %d\n", multi[4].revents == POLLOUT); - - return 0; - } - ''' - expected = r''' - ret: 4 - errno: 0 - multi[0].revents: 1 - multi[1].revents: 1 - multi[2].revents: 1 - multi[3].revents: 1 - multi[4].revents: 1 - ''' - self.do_run(src, re.sub('(^|\n)\s+', '\\1', expected), post_build=add_pre_run, extra_emscripten_args=['-H', 'libc/fcntl.h,poll.h']) - - def test_statvfs(self): - src = r''' - #include - #include - #include - - int main() { - struct statvfs s; - - printf("result: %d\n", statvfs("/test", &s)); - printf("errno: %d\n", errno); - - printf("f_bsize: %lu\n", s.f_bsize); - printf("f_frsize: %lu\n", s.f_frsize); - printf("f_blocks: %lu\n", s.f_blocks); - printf("f_bfree: %lu\n", s.f_bfree); - printf("f_bavail: %lu\n", s.f_bavail); - printf("f_files: %d\n", s.f_files > 5); - printf("f_ffree: %lu\n", s.f_ffree); - printf("f_favail: %lu\n", s.f_favail); - printf("f_fsid: %lu\n", s.f_fsid); - printf("f_flag: %lu\n", s.f_flag); - printf("f_namemax: %lu\n", s.f_namemax); - - return 0; - } - ''' - expected = r''' - result: 0 - errno: 0 - f_bsize: 4096 - f_frsize: 4096 - f_blocks: 1000000 - f_bfree: 500000 - f_bavail: 500000 - f_files: 1 - f_ffree: 1000000 - f_favail: 1000000 - f_fsid: 42 - f_flag: 2 - f_namemax: 255 - ''' - self.do_run(src, re.sub('(^|\n)\s+', '\\1', expected)) - - def test_libgen(self): - src = r''' - #include - #include - - int main() { - char p1[16] = "/usr/lib", p1x[16] = "/usr/lib"; - printf("%s -> ", p1); - printf("%s : %s\n", dirname(p1x), basename(p1)); - - char p2[16] = "/usr", p2x[16] = "/usr"; - printf("%s -> ", p2); - printf("%s : %s\n", dirname(p2x), basename(p2)); - - char p3[16] = "/usr/", p3x[16] = "/usr/"; - printf("%s -> ", p3); - printf("%s : %s\n", dirname(p3x), basename(p3)); - - char p4[16] = "/usr/lib///", p4x[16] = "/usr/lib///"; - printf("%s -> ", p4); - printf("%s : %s\n", dirname(p4x), basename(p4)); - - char p5[16] = "/", p5x[16] = "/"; - printf("%s -> ", p5); - printf("%s : %s\n", dirname(p5x), basename(p5)); - - char p6[16] = "///", p6x[16] = "///"; - printf("%s -> ", p6); - printf("%s : %s\n", dirname(p6x), basename(p6)); - - char p7[16] = "/usr/../lib/..", p7x[16] = "/usr/../lib/.."; - printf("%s -> ", p7); - printf("%s : %s\n", dirname(p7x), basename(p7)); - - char p8[16] = "", p8x[16] = ""; - printf("(empty) -> %s : %s\n", dirname(p8x), basename(p8)); - - printf("(null) -> %s : %s\n", dirname(0), basename(0)); - - return 0; - } - ''' - expected = ''' - /usr/lib -> /usr : lib - /usr -> / : usr - /usr/ -> / : usr - /usr/lib/// -> /usr : lib - / -> / : / - /// -> / : / - /usr/../lib/.. -> /usr/../lib : .. - (empty) -> . : . - (null) -> . : . - ''' - self.do_run(src, re.sub('(^|\n)\s+', '\\1', expected)) - - def test_utime(self): - src = open(path_from_root('tests', 'utime', 'test_utime.c'), 'r').read() - self.do_run(src, 'success', force_c=True) - - def test_utf(self): - self.banned_js_engines = [SPIDERMONKEY_ENGINE] # only node handles utf well - Settings.EXPORTED_FUNCTIONS = ['_main', '_malloc'] - - src = r''' - #include - #include - - int main() { - char *c = "μ†ℱ ╋ℯ╳╋"; - printf("%d %d %d %d %s\n", c[0]&0xff, c[1]&0xff, c[2]&0xff, c[3]&0xff, c); - emscripten_run_script("cheez = _malloc(100);" - "Module.writeStringToMemory(\"μ†ℱ ╋ℯ╳╋\", cheez);" - "Module.print([Pointer_stringify(cheez), Module.getValue(cheez, 'i8')&0xff, Module.getValue(cheez+1, 'i8')&0xff, Module.getValue(cheez+2, 'i8')&0xff, Module.getValue(cheez+3, 'i8')&0xff, ]);"); - } - ''' - self.do_run(src, '206 188 226 128 μ†ℱ ╋ℯ╳╋\nμ†ℱ ╋ℯ╳╋,206,188,226,128\n'); - - def test_direct_string_constant_usage(self): - if self.emcc_args is None: return self.skip('requires libcxx') - - src = ''' - #include - template - void printText( const char (&text)[ i ] ) - { - std::cout << text; - } - int main() - { - printText( "some string constant" ); - return 0; - } - ''' - self.do_run(src, "some string constant") - - def test_std_cout_new(self): - if self.emcc_args is None: return self.skip('requires emcc') - - src = ''' - #include - - struct NodeInfo { //structure that we want to transmit to our shaders - float x; - float y; - float s; - float c; - }; - const int nbNodes = 100; - NodeInfo * data = new NodeInfo[nbNodes]; //our data that will be transmitted using float texture. - - template - void printText( const char (&text)[ i ] ) - { - std::cout << text << std::endl; - } - - int main() - { - printText( "some string constant" ); - return 0; - } - ''' - - self.do_run(src, "some string constant") - - def test_istream(self): - if self.emcc_args is None: return self.skip('requires libcxx') - - src = ''' - #include - #include - #include - - int main() - { - std::string mystring("1 2 3"); - std::istringstream is(mystring); - int one, two, three; - - is >> one >> two >> three; - - printf( "%i %i %i", one, two, three ); - } - ''' - for linkable in [0]:#, 1]: - print linkable - Settings.LINKABLE = linkable # regression check for issue #273 - self.do_run(src, "1 2 3") - - def test_fs_base(self): - Settings.INCLUDE_FULL_LIBRARY = 1 - try: - addJS = ''' -def process(filename): - import tools.shared as shared - src = open(filename, 'r').read().replace('FS.init();', '').replace( # Disable normal initialization, replace with ours - '// {{PRE_RUN_ADDITIONS}}', - open(shared.path_from_root('tests', 'filesystem', 'src.js'), 'r').read()) - open(filename, 'w').write(src) -''' - src = 'int main() {return 0;}\n' - expected = open(path_from_root('tests', 'filesystem', 'output.txt'), 'r').read() - self.do_run(src, expected, post_build=addJS, extra_emscripten_args=['-H', 'libc/fcntl.h,libc/sys/unistd.h,poll.h,libc/math.h,libc/langinfo.h,libc/time.h']) - finally: - Settings.INCLUDE_FULL_LIBRARY = 0 - - def test_unistd_access(self): - add_pre_run = ''' -def process(filename): - import tools.shared as shared - src = open(filename, 'r').read().replace( - '// {{PRE_RUN_ADDITIONS}}', - open(shared.path_from_root('tests', 'unistd', 'access.js'), 'r').read() - ) - open(filename, 'w').write(src) -''' - src = open(path_from_root('tests', 'unistd', 'access.c'), 'r').read() - expected = open(path_from_root('tests', 'unistd', 'access.out'), 'r').read() - self.do_run(src, expected, post_build=add_pre_run) - - def test_unistd_curdir(self): - add_pre_run = ''' -def process(filename): - import tools.shared as shared - src = open(filename, 'r').read().replace( - '// {{PRE_RUN_ADDITIONS}}', - open(shared.path_from_root('tests', 'unistd', 'curdir.js'), 'r').read() - ) - open(filename, 'w').write(src) -''' - src = open(path_from_root('tests', 'unistd', 'curdir.c'), 'r').read() - expected = open(path_from_root('tests', 'unistd', 'curdir.out'), 'r').read() - self.do_run(src, expected, post_build=add_pre_run) - - def test_unistd_close(self): - src = open(path_from_root('tests', 'unistd', 'close.c'), 'r').read() - expected = open(path_from_root('tests', 'unistd', 'close.out'), 'r').read() - self.do_run(src, expected) - - def test_unistd_confstr(self): - src = open(path_from_root('tests', 'unistd', 'confstr.c'), 'r').read() - expected = open(path_from_root('tests', 'unistd', 'confstr.out'), 'r').read() - self.do_run(src, expected, extra_emscripten_args=['-H', 'libc/unistd.h']) - - def test_unistd_ttyname(self): - src = open(path_from_root('tests', 'unistd', 'ttyname.c'), 'r').read() - self.do_run(src, 'success', force_c=True) - - def test_unistd_dup(self): - src = open(path_from_root('tests', 'unistd', 'dup.c'), 'r').read() - expected = open(path_from_root('tests', 'unistd', 'dup.out'), 'r').read() - self.do_run(src, expected) - - def test_unistd_pathconf(self): - src = open(path_from_root('tests', 'unistd', 'pathconf.c'), 'r').read() - expected = open(path_from_root('tests', 'unistd', 'pathconf.out'), 'r').read() - self.do_run(src, expected) - - def test_unistd_truncate(self): - add_pre_run = ''' -def process(filename): - import tools.shared as shared - src = open(filename, 'r').read().replace( - '// {{PRE_RUN_ADDITIONS}}', - open(shared.path_from_root('tests', 'unistd', 'truncate.js'), 'r').read() - ) - open(filename, 'w').write(src) -''' - src = open(path_from_root('tests', 'unistd', 'truncate.c'), 'r').read() - expected = open(path_from_root('tests', 'unistd', 'truncate.out'), 'r').read() - self.do_run(src, expected, post_build=add_pre_run) - - def test_unistd_swab(self): - src = open(path_from_root('tests', 'unistd', 'swab.c'), 'r').read() - expected = open(path_from_root('tests', 'unistd', 'swab.out'), 'r').read() - self.do_run(src, expected) - - def test_unistd_isatty(self): - src = open(path_from_root('tests', 'unistd', 'isatty.c'), 'r').read() - self.do_run(src, 'success', force_c=True) - - def test_unistd_sysconf(self): - src = open(path_from_root('tests', 'unistd', 'sysconf.c'), 'r').read() - expected = open(path_from_root('tests', 'unistd', 'sysconf.out'), 'r').read() - self.do_run(src, expected) - - def test_unistd_login(self): - src = open(path_from_root('tests', 'unistd', 'login.c'), 'r').read() - expected = open(path_from_root('tests', 'unistd', 'login.out'), 'r').read() - self.do_run(src, expected) - - def test_unistd_unlink(self): - src = open(path_from_root('tests', 'unistd', 'unlink.c'), 'r').read() - self.do_run(src, 'success', force_c=True) - - def test_unistd_links(self): - add_pre_run = ''' -def process(filename): - import tools.shared as shared - src = open(filename, 'r').read().replace( - '// {{PRE_RUN_ADDITIONS}}', - open(shared.path_from_root('tests', 'unistd', 'links.js'), 'r').read() - ) - open(filename, 'w').write(src) -''' - src = open(path_from_root('tests', 'unistd', 'links.c'), 'r').read() - expected = open(path_from_root('tests', 'unistd', 'links.out'), 'r').read() - self.do_run(src, expected, post_build=add_pre_run) - - def test_unistd_sleep(self): - src = open(path_from_root('tests', 'unistd', 'sleep.c'), 'r').read() - expected = open(path_from_root('tests', 'unistd', 'sleep.out'), 'r').read() - self.do_run(src, expected) - - def test_unistd_io(self): - add_pre_run = ''' -def process(filename): - import tools.shared as shared - src = open(filename, 'r').read().replace( - '// {{PRE_RUN_ADDITIONS}}', - open(shared.path_from_root('tests', 'unistd', 'io.js'), 'r').read() - ) - open(filename, 'w').write(src) -''' - src = open(path_from_root('tests', 'unistd', 'io.c'), 'r').read() - expected = open(path_from_root('tests', 'unistd', 'io.out'), 'r').read() - self.do_run(src, expected, post_build=add_pre_run) - - def test_unistd_misc(self): - src = open(path_from_root('tests', 'unistd', 'misc.c'), 'r').read() - expected = open(path_from_root('tests', 'unistd', 'misc.out'), 'r').read() - self.do_run(src, expected) - - def test_uname(self): - src = r''' - #include - #include - - int main() { - struct utsname u; - printf("ret: %d\n", uname(&u)); - printf("sysname: %s\n", u.sysname); - printf("nodename: %s\n", u.nodename); - printf("release: %s\n", u.release); - printf("version: %s\n", u.version); - printf("machine: %s\n", u.machine); - printf("invalid: %d\n", uname(0)); - return 0; - } - ''' - expected = ''' - ret: 0 - sysname: Emscripten - nodename: emscripten - release: 1.0 - version: #1 - machine: x86-JS - ''' - self.do_run(src, re.sub('(^|\n)\s+', '\\1', expected)) - - def test_env(self): - src = open(path_from_root('tests', 'env', 'src.c'), 'r').read() - expected = open(path_from_root('tests', 'env', 'output.txt'), 'r').read() - self.do_run(src, expected) - - def test_systypes(self): - src = open(path_from_root('tests', 'systypes', 'src.c'), 'r').read() - expected = open(path_from_root('tests', 'systypes', 'output.txt'), 'r').read() - self.do_run(src, expected) - - def test_getloadavg(self): - src = r''' - #include - #include - - int main() { - double load[5] = {42.13, 42.13, 42.13, 42.13, 42.13}; - printf("ret: %d\n", getloadavg(load, 5)); - printf("load[0]: %.3lf\n", load[0]); - printf("load[1]: %.3lf\n", load[1]); - printf("load[2]: %.3lf\n", load[2]); - printf("load[3]: %.3lf\n", load[3]); - printf("load[4]: %.3lf\n", load[4]); - return 0; - } - ''' - expected = ''' - ret: 3 - load[0]: 0.100 - load[1]: 0.100 - load[2]: 0.100 - load[3]: 42.130 - load[4]: 42.130 - ''' - self.do_run(src, re.sub('(^|\n)\s+', '\\1', expected)) - - def test_inet(self): - src = r''' - #include - #include - - int main() { - printf("*%x,%x,%x,%x,%x,%x*\n", htonl(0xa1b2c3d4), htonl(0xfe3572e0), htonl(0x07abcdf0), htons(0xabcd), ntohl(0x43211234), ntohs(0xbeaf)); - in_addr_t i = inet_addr("190.180.10.78"); - printf("%x\n", i); - return 0; - } - ''' - self.do_run(src, '*d4c3b2a1,e07235fe,f0cdab07,cdab,34122143,afbe*\n4e0ab4be\n') - - def test_inet2(self): - src = r''' - #include - #include - - int main() { - struct in_addr x, x2; - int *y = (int*)&x; - *y = 0x12345678; - printf("%s\n", inet_ntoa(x)); - int r = inet_aton(inet_ntoa(x), &x2); - printf("%s\n", inet_ntoa(x2)); - return 0; - } - ''' - self.do_run(src, '120.86.52.18\n120.86.52.18\n') - - def test_inet3(self): - src = r''' - #include - #include - #include - int main() { - char dst[64]; - struct in_addr x, x2; - int *y = (int*)&x; - *y = 0x12345678; - printf("%s\n", inet_ntop(AF_INET,&x,dst,sizeof dst)); - int r = inet_aton(inet_ntoa(x), &x2); - printf("%s\n", inet_ntop(AF_INET,&x2,dst,sizeof dst)); - return 0; - } - ''' - self.do_run(src, '120.86.52.18\n120.86.52.18\n') - - def test_inet4(self): - if Settings.USE_TYPED_ARRAYS != 2: return self.skip('requires ta2') - - src = r''' - #include - #include - #include - - void test(char *test_addr){ - char str[40]; - struct in6_addr addr; - unsigned char *p = (unsigned char*)&addr; - int ret; - ret = inet_pton(AF_INET6,test_addr,&addr); - if(ret == -1) return; - if(ret == 0) return; - if(inet_ntop(AF_INET6,&addr,str,sizeof(str)) == NULL ) return; - printf("%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x - %s\n", - p[0],p[1],p[2],p[3],p[4],p[5],p[6],p[7],p[8],p[9],p[10],p[11],p[12],p[13],p[14],p[15],str); - } - int main(){ - test("::"); - test("::1"); - test("::1.2.3.4"); - test("::17.18.19.20"); - test("::ffff:1.2.3.4"); - test("1::ffff"); - test("::255.255.255.255"); - test("0:ff00:1::"); - test("0:ff::"); - test("abcd::"); - test("ffff::a"); - test("ffff::a:b"); - test("ffff::a:b:c"); - test("ffff::a:b:c:d"); - test("ffff::a:b:c:d:e"); - test("::1:2:0:0:0"); - test("0:0:1:2:3::"); - test("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"); - test("1::255.255.255.255"); - - //below should fail and not produce results.. - test("1.2.3.4"); - test(""); - test("-"); - } - ''' - self.do_run(src, - "0000:0000:0000:0000:0000:0000:0000:0000 - ::\n" - "0000:0000:0000:0000:0000:0000:0000:0001 - ::1\n" - "0000:0000:0000:0000:0000:0000:0102:0304 - ::1.2.3.4\n" - "0000:0000:0000:0000:0000:0000:1112:1314 - ::17.18.19.20\n" - "0000:0000:0000:0000:0000:ffff:0102:0304 - ::ffff:1.2.3.4\n" - "0001:0000:0000:0000:0000:0000:0000:ffff - 1::ffff\n" - "0000:0000:0000:0000:0000:0000:ffff:ffff - ::255.255.255.255\n" - "0000:ff00:0001:0000:0000:0000:0000:0000 - 0:ff00:1::\n" - "0000:00ff:0000:0000:0000:0000:0000:0000 - 0:ff::\n" - "abcd:0000:0000:0000:0000:0000:0000:0000 - abcd::\n" - "ffff:0000:0000:0000:0000:0000:0000:000a - ffff::a\n" - "ffff:0000:0000:0000:0000:0000:000a:000b - ffff::a:b\n" - "ffff:0000:0000:0000:0000:000a:000b:000c - ffff::a:b:c\n" - "ffff:0000:0000:0000:000a:000b:000c:000d - ffff::a:b:c:d\n" - "ffff:0000:0000:000a:000b:000c:000d:000e - ffff::a:b:c:d:e\n" - "0000:0000:0000:0001:0002:0000:0000:0000 - ::1:2:0:0:0\n" - "0000:0000:0001:0002:0003:0000:0000:0000 - 0:0:1:2:3::\n" - "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff - ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff\n" - "0001:0000:0000:0000:0000:0000:ffff:ffff - 1::ffff:ffff\n" - ) - - def test_gethostbyname(self): - if Settings.USE_TYPED_ARRAYS != 2: return self.skip("assume t2 in gethostbyname") - - src = r''' - #include - #include - - void test(char *hostname) { - hostent *host = gethostbyname(hostname); - if (!host) { - printf("no such thing\n"); - return; - } - printf("%s : %d : %d\n", host->h_name, host->h_addrtype, host->h_length); - char **name = host->h_aliases; - while (*name) { - printf("- %s\n", *name); - name++; - } - name = host->h_addr_list; - while (name && *name) { - printf("* "); - for (int i = 0; i < host->h_length; i++) - printf("%d.", (*name)[i]); - printf("\n"); - name++; - } - } - - int main() { - test("www.cheezburger.com"); - test("fail.on.this.never.work"); // we will "work" on this - because we are just making aliases of names to ips - test("localhost"); - return 0; - } - ''' - self.do_run(src, '''www.cheezburger.com : 2 : 4 -* -84.29.1.0. -fail.on.this.never.work : 2 : 4 -* -84.29.2.0. -localhost : 2 : 4 -* -84.29.3.0. -''') - - def test_799(self): - src = open(path_from_root('tests', '799.cpp'), 'r').read() - self.do_run(src, '''Set PORT family: 0, port: 3979 -Get PORT family: 0 -PORT: 3979 -''') - - def test_ctype(self): - # The bit fiddling done by the macros using __ctype_b_loc requires this. - Settings.CORRECT_SIGNS = 1 - src = open(path_from_root('tests', 'ctype', 'src.c'), 'r').read() - expected = open(path_from_root('tests', 'ctype', 'output.txt'), 'r').read() - self.do_run(src, expected) - - def test_strcasecmp(self): - src = r''' - #include - #include - int sign(int x) { - if (x < 0) return -1; - if (x > 0) return 1; - return 0; - } - int main() { - printf("*\n"); - - printf("%d\n", sign(strcasecmp("hello", "hello"))); - printf("%d\n", sign(strcasecmp("hello1", "hello"))); - printf("%d\n", sign(strcasecmp("hello", "hello1"))); - printf("%d\n", sign(strcasecmp("hello1", "hello1"))); - printf("%d\n", sign(strcasecmp("iello", "hello"))); - printf("%d\n", sign(strcasecmp("hello", "iello"))); - printf("%d\n", sign(strcasecmp("A", "hello"))); - printf("%d\n", sign(strcasecmp("Z", "hello"))); - printf("%d\n", sign(strcasecmp("a", "hello"))); - printf("%d\n", sign(strcasecmp("z", "hello"))); - printf("%d\n", sign(strcasecmp("hello", "a"))); - printf("%d\n", sign(strcasecmp("hello", "z"))); - - printf("%d\n", sign(strcasecmp("Hello", "hello"))); - printf("%d\n", sign(strcasecmp("Hello1", "hello"))); - printf("%d\n", sign(strcasecmp("Hello", "hello1"))); - printf("%d\n", sign(strcasecmp("Hello1", "hello1"))); - printf("%d\n", sign(strcasecmp("Iello", "hello"))); - printf("%d\n", sign(strcasecmp("Hello", "iello"))); - printf("%d\n", sign(strcasecmp("A", "hello"))); - printf("%d\n", sign(strcasecmp("Z", "hello"))); - printf("%d\n", sign(strcasecmp("a", "hello"))); - printf("%d\n", sign(strcasecmp("z", "hello"))); - printf("%d\n", sign(strcasecmp("Hello", "a"))); - printf("%d\n", sign(strcasecmp("Hello", "z"))); - - printf("%d\n", sign(strcasecmp("hello", "Hello"))); - printf("%d\n", sign(strcasecmp("hello1", "Hello"))); - printf("%d\n", sign(strcasecmp("hello", "Hello1"))); - printf("%d\n", sign(strcasecmp("hello1", "Hello1"))); - printf("%d\n", sign(strcasecmp("iello", "Hello"))); - printf("%d\n", sign(strcasecmp("hello", "Iello"))); - printf("%d\n", sign(strcasecmp("A", "Hello"))); - printf("%d\n", sign(strcasecmp("Z", "Hello"))); - printf("%d\n", sign(strcasecmp("a", "Hello"))); - printf("%d\n", sign(strcasecmp("z", "Hello"))); - printf("%d\n", sign(strcasecmp("hello", "a"))); - printf("%d\n", sign(strcasecmp("hello", "z"))); - - printf("%d\n", sign(strcasecmp("Hello", "Hello"))); - printf("%d\n", sign(strcasecmp("Hello1", "Hello"))); - printf("%d\n", sign(strcasecmp("Hello", "Hello1"))); - printf("%d\n", sign(strcasecmp("Hello1", "Hello1"))); - printf("%d\n", sign(strcasecmp("Iello", "Hello"))); - printf("%d\n", sign(strcasecmp("Hello", "Iello"))); - printf("%d\n", sign(strcasecmp("A", "Hello"))); - printf("%d\n", sign(strcasecmp("Z", "Hello"))); - printf("%d\n", sign(strcasecmp("a", "Hello"))); - printf("%d\n", sign(strcasecmp("z", "Hello"))); - printf("%d\n", sign(strcasecmp("Hello", "a"))); - printf("%d\n", sign(strcasecmp("Hello", "z"))); - - printf("%d\n", sign(strncasecmp("hello", "hello", 3))); - printf("%d\n", sign(strncasecmp("hello1", "hello", 3))); - printf("%d\n", sign(strncasecmp("hello", "hello1", 3))); - printf("%d\n", sign(strncasecmp("hello1", "hello1", 3))); - printf("%d\n", sign(strncasecmp("iello", "hello", 3))); - printf("%d\n", sign(strncasecmp("hello", "iello", 3))); - printf("%d\n", sign(strncasecmp("A", "hello", 3))); - printf("%d\n", sign(strncasecmp("Z", "hello", 3))); - printf("%d\n", sign(strncasecmp("a", "hello", 3))); - printf("%d\n", sign(strncasecmp("z", "hello", 3))); - printf("%d\n", sign(strncasecmp("hello", "a", 3))); - printf("%d\n", sign(strncasecmp("hello", "z", 3))); - - printf("*\n"); - - return 0; - } - ''' - self.do_run(src, '''*\n0\n1\n-1\n0\n1\n-1\n-1\n1\n-1\n1\n1\n-1\n0\n1\n-1\n0\n1\n-1\n-1\n1\n-1\n1\n1\n-1\n0\n1\n-1\n0\n1\n-1\n-1\n1\n-1\n1\n1\n-1\n0\n1\n-1\n0\n1\n-1\n-1\n1\n-1\n1\n1\n-1\n0\n0\n0\n0\n1\n-1\n-1\n1\n-1\n1\n1\n-1\n*\n''') - - def test_atomic(self): - src = ''' - #include - int main() { - int x = 10; - int y = __sync_add_and_fetch(&x, 5); - printf("*%d,%d*\\n", x, y); - x = 10; - y = __sync_fetch_and_add(&x, 5); - printf("*%d,%d*\\n", x, y); - x = 10; - y = __sync_lock_test_and_set(&x, 6); - printf("*%d,%d*\\n", x, y); - x = 10; - y = __sync_bool_compare_and_swap(&x, 9, 7); - printf("*%d,%d*\\n", x, y); - y = __sync_bool_compare_and_swap(&x, 10, 7); - printf("*%d,%d*\\n", x, y); - return 0; - } - ''' - - self.do_run(src, '*15,15*\n*15,10*\n*6,10*\n*10,0*\n*7,1*') - - def test_phiundef(self): - src = r''' -#include -#include - -static int state; - -struct my_struct { - union { - struct { - unsigned char a; - unsigned char b; - } c; - unsigned int d; - } e; - unsigned int f; -}; - -int main(int argc, char **argv) { - struct my_struct r; - - state = 0; - - for (int i=0;i - - int main() - { - std::cout << "hello world" << std::endl << 77 << "." << std::endl; - return 0; - } - ''' - - # FIXME: should not have so many newlines in output here - self.do_run(src, 'hello world\n77.\n') - - def test_stdvec(self): - if self.emcc_args is None: return self.skip('requires emcc') - src = ''' - #include - #include - - struct S { - int a; - float b; - }; - - void foo(int a, float b) - { - printf("%d:%.2f\\n", a, b); - } - - int main ( int argc, char *argv[] ) - { - std::vector ar; - S s; - - s.a = 789; - s.b = 123.456f; - ar.push_back(s); - - s.a = 0; - s.b = 100.1f; - ar.push_back(s); - - foo(ar[0].a, ar[0].b); - foo(ar[1].a, ar[1].b); - } - ''' - - self.do_run(src, '789:123.46\n0:100.1') - - def test_reinterpreted_ptrs(self): - if self.emcc_args is None: return self.skip('needs emcc and libc') - - src = r''' -#include - -class Foo { -private: - float bar; -public: - int baz; - - Foo(): bar(0), baz(4711) {}; - - int getBar() const; -}; - -int Foo::getBar() const { - return this->bar; -}; - -const Foo *magic1 = reinterpret_cast(0xDEAD111F); -const Foo *magic2 = reinterpret_cast(0xDEAD888F); - -static void runTest() { - - const Foo *a = new Foo(); - const Foo *b = a; - - if (a->getBar() == 0) { - if (a->baz == 4712) - b = magic1; - else - b = magic2; - } - - printf("%s\n", (b == magic1 ? "magic1" : (b == magic2 ? "magic2" : "neither"))); -}; - -extern "C" { - int main(int argc, char **argv) { - runTest(); - } -} -''' - self.do_run(src, 'magic2') - - def test_jansson(self): - return self.skip('currently broken') - - if Settings.USE_TYPED_ARRAYS != 2: return self.skip('requires ta2') - if Settings.SAFE_HEAP: return self.skip('jansson is not safe-heap safe') - - src = ''' - #include - #include - #include - - int main() - { - const char* jsonString = "{\\"key\\": \\"value\\",\\"array\\": [\\"array_item1\\",\\"array_item2\\",\\"array_item3\\"],\\"dict\\":{\\"number\\": 3,\\"float\\": 2.2}}"; - - json_error_t error; - json_t *root = json_loadb(jsonString, strlen(jsonString), 0, &error); - - if(!root) { - printf("Node `root` is `null`."); - return 0; - } - - if(!json_is_object(root)) { - printf("Node `root` is no object."); - return 0; - } - - printf("%s\\n", json_string_value(json_object_get(root, "key"))); - - json_t *array = json_object_get(root, "array"); - if(!array) { - printf("Node `array` is `null`."); - return 0; - } - - if(!json_is_array(array)) { - printf("Node `array` is no array."); - return 0; - } - - for(size_t i=0; i1) - - def test_raytrace(self): - if self.emcc_args is None: return self.skip('requires emcc') - if Settings.USE_TYPED_ARRAYS == 2: return self.skip('Relies on double value rounding, extremely sensitive') - - src = open(path_from_root('tests', 'raytrace.cpp'), 'r').read().replace('double', 'float') - output = open(path_from_root('tests', 'raytrace.ppm'), 'r').read() - self.do_run(src, output, ['3', '16'])#, build_ll_hook=self.do_autodebug) - - def test_fasta(self): - if self.emcc_args is None: return self.skip('requires emcc') - results = [ (1,'''GG*ctt**tgagc*'''), (20,'''GGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTT*cttBtatcatatgctaKggNcataaaSatgtaaaDcDRtBggDtctttataattcBgtcg**tacgtgtagcctagtgtttgtgttgcgttatagtctatttgtggacacagtatggtcaaa**tgacgtcttttgatctgacggcgttaacaaagatactctg*'''), -(50,'''GGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGA*TCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACAT*cttBtatcatatgctaKggNcataaaSatgtaaaDcDRtBggDtctttataattcBgtcg**tactDtDagcctatttSVHtHttKtgtHMaSattgWaHKHttttagacatWatgtRgaaa**NtactMcSMtYtcMgRtacttctWBacgaa**agatactctgggcaacacacatacttctctcatgttgtttcttcggacctttcataacct**ttcctggcacatggttagctgcacatcacaggattgtaagggtctagtggttcagtgagc**ggaatatcattcgtcggtggtgttaatctatctcggtgtagcttataaatgcatccgtaa**gaatattatgtttatttgtcggtacgttcatggtagtggtgtcgccgatttagacgtaaa**ggcatgtatg*''') ] - for i, j in results: - src = open(path_from_root('tests', 'fasta.cpp'), 'r').read() - self.do_run(src, j, [str(i)], lambda x, err: x.replace('\n', '*'), no_build=i>1) - - def test_whets(self): - if not Settings.ASM_JS: return self.skip('mainly a test for asm validation here') - self.do_run(open(path_from_root('tests', 'whets.cpp')).read(), 'Single Precision C Whetstone Benchmark') - - def test_dlmalloc(self): - if self.emcc_args is None: self.emcc_args = [] # dlmalloc auto-inclusion is only done if we use emcc - - self.banned_js_engines = [NODE_JS] # slower, and fail on 64-bit - Settings.CORRECT_SIGNS = 2 - Settings.CORRECT_SIGNS_LINES = ['src.cpp:' + str(i+4) for i in [4816, 4191, 4246, 4199, 4205, 4235, 4227]] - Settings.TOTAL_MEMORY = 128*1024*1024 # needed with typed arrays - - src = open(path_from_root('system', 'lib', 'dlmalloc.c'), 'r').read() + '\n\n\n' + open(path_from_root('tests', 'dlmalloc_test.c'), 'r').read() - self.do_run(src, '*1,0*', ['200', '1']) - self.do_run(src, '*400,0*', ['400', '400'], no_build=True) - - # Linked version - src = open(path_from_root('tests', 'dlmalloc_test.c'), 'r').read() - self.do_run(src, '*1,0*', ['200', '1'], extra_emscripten_args=['-m']) - self.do_run(src, '*400,0*', ['400', '400'], extra_emscripten_args=['-m'], no_build=True) - - if self.emcc_args == []: # TODO: do this in other passes too, passing their opts into emcc - # emcc should build in dlmalloc automatically, and do all the sign correction etc. for it - - try_delete(os.path.join(self.get_dir(), 'src.cpp.o.js')) - output = Popen([PYTHON, EMCC, path_from_root('tests', 'dlmalloc_test.c'), '-s', 'TOTAL_MEMORY=' + str(128*1024*1024), - '-o', os.path.join(self.get_dir(), 'src.cpp.o.js')], stdout=PIPE, stderr=self.stderr_redirect).communicate() - - self.do_run('x', '*1,0*', ['200', '1'], no_build=True) - self.do_run('x', '*400,0*', ['400', '400'], no_build=True) - - # The same for new and all its variants - src = open(path_from_root('tests', 'new.cpp')).read() - for new, delete in [ - ('malloc(100)', 'free'), - ('new char[100]', 'delete[]'), - ('new Structy', 'delete'), - ('new int', 'delete'), - ('new Structy[10]', 'delete[]'), - ]: - self.do_run(src.replace('{{{ NEW }}}', new).replace('{{{ DELETE }}}', delete), '*1,0*') - - def test_dlmalloc_partial(self): - if self.emcc_args is None: return self.skip('only emcc will link in dlmalloc') - # present part of the symbols of dlmalloc, not all - src = open(path_from_root('tests', 'new.cpp')).read().replace('{{{ NEW }}}', 'new int').replace('{{{ DELETE }}}', 'delete') + ''' -void * -operator new(size_t size) -{ - printf("new %d!\\n", size); - return malloc(size); -} -''' - self.do_run(src, 'new 4!\n*1,0*') - - def test_dlmalloc_partial_2(self): - if self.emcc_args is None or 'SAFE_HEAP' in str(self.emcc_args) or 'CHECK_HEAP_ALIGN' in str(self.emcc_args): return self.skip('only emcc will link in dlmalloc, and we do unsafe stuff') - # present part of the symbols of dlmalloc, not all. malloc is harder to link than new which is weak. - src = r''' - #include - #include - void *malloc(size_t size) - { - return (void*)123; - } - int main() { - void *x = malloc(10); - printf("got %p\n", x); - free(x); - printf("freed the faker\n"); - return 1; - } -''' - self.do_run(src, 'got 0x7b\nfreed') - - def test_libcxx(self): - if self.emcc_args is None: return self.skip('requires emcc') - self.do_run(open(path_from_root('tests', 'hashtest.cpp')).read(), - 'june -> 30\nPrevious (in alphabetical order) is july\nNext (in alphabetical order) is march') - - self.do_run(''' - #include - #include - int main() { - std::set *fetchOriginatorNums = new std::set(); - fetchOriginatorNums->insert(171); - printf("hello world\\n"); - return 0; - } - ''', 'hello world'); - - def test_typeid(self): - self.do_run(r''' - #include - #include - #include - int main() { - printf("*\n"); - #define MAX 100 - int ptrs[MAX]; - int groups[MAX]; - memset(ptrs, 0, MAX*sizeof(int)); - memset(groups, 0, MAX*sizeof(int)); - int next_group = 1; - #define TEST(X) { \ - int ptr = (int)&typeid(X); \ - int group = 0; \ - int i; \ - for (i = 0; i < MAX; i++) { \ - if (!groups[i]) break; \ - if (ptrs[i] == ptr) { \ - group = groups[i]; \ - break; \ - } \ - } \ - if (!group) { \ - groups[i] = group = next_group++; \ - ptrs[i] = ptr; \ - } \ - printf("%s:%d\n", #X, group); \ - } - TEST(int); - TEST(unsigned int); - TEST(unsigned); - TEST(signed int); - TEST(long); - TEST(unsigned long); - TEST(signed long); - TEST(long long); - TEST(unsigned long long); - TEST(signed long long); - TEST(short); - TEST(unsigned short); - TEST(signed short); - TEST(char); - TEST(unsigned char); - TEST(signed char); - TEST(float); - TEST(double); - TEST(long double); - TEST(void); - TEST(void*); - printf("*\n"); - } - ''', '''* -int:1 -unsigned int:2 -unsigned:2 -signed int:1 -long:3 -unsigned long:4 -signed long:3 -long long:5 -unsigned long long:6 -signed long long:5 -short:7 -unsigned short:8 -signed short:7 -char:9 -unsigned char:10 -signed char:11 -float:12 -double:13 -long double:14 -void:15 -void*:16 -* -'''); - - def test_static_variable(self): - if self.emcc_args is None: Settings.SAFE_HEAP = 0 # LLVM mixes i64 and i8 in the guard check - src = ''' - #include - - struct DATA - { - int value; - - DATA() - { - value = 0; - } - }; - - DATA & GetData() - { - static DATA data; - - return data; - } - - int main() - { - GetData().value = 10; - printf( "value:%i", GetData().value ); - } - ''' - self.do_run(src, 'value:10') - - def test_fakestat(self): - src = r''' - #include - struct stat { int x, y; }; - int main() { - stat s; - s.x = 10; - s.y = 22; - printf("*%d,%d*\n", s.x, s.y); - } - ''' - self.do_run(src, '*10,22*') - - def test_mmap(self): - if self.emcc_args is None: return self.skip('requires emcc') - - Settings.TOTAL_MEMORY = 128*1024*1024 - - src = ''' - #include - #include - #include - - int main(int argc, char *argv[]) { - for (int i = 0; i < 10; i++) { - int* map = (int*)mmap(0, 5000, PROT_READ | PROT_WRITE, - MAP_SHARED | MAP_ANON, -1, 0); - /* TODO: Should we align to 4k? - assert(((int)map) % 4096 == 0); // aligned - */ - assert(munmap(map, 5000) == 0); - } - - const int NUM_BYTES = 8 * 1024 * 1024; - const int NUM_INTS = NUM_BYTES / sizeof(int); - - int* map = (int*)mmap(0, NUM_BYTES, PROT_READ | PROT_WRITE, - MAP_SHARED | MAP_ANON, -1, 0); - assert(map != MAP_FAILED); - - int i; - - for (i = 0; i < NUM_INTS; i++) { - map[i] = i; - } - - for (i = 0; i < NUM_INTS; i++) { - assert(map[i] == i); - } - - assert(munmap(map, NUM_BYTES) == 0); - - printf("hello,world"); - return 0; - } - ''' - self.do_run(src, 'hello,world') - self.do_run(src, 'hello,world', force_c=True) - - def test_mmap_file(self): - if self.emcc_args is None: return self.skip('requires emcc') - self.emcc_args += ['--embed-file', 'data.dat'] - - open(self.in_dir('data.dat'), 'w').write('data from the file ' + ('.' * 9000)) - - src = r''' - #include - #include - - int main() { - printf("*\n"); - FILE *f = fopen("data.dat", "r"); - char *m; - m = (char*)mmap(NULL, 9000, PROT_READ, MAP_PRIVATE, fileno(f), 0); - for (int i = 0; i < 20; i++) putchar(m[i]); - munmap(m, 9000); - printf("\n"); - m = (char*)mmap(NULL, 9000, PROT_READ, MAP_PRIVATE, fileno(f), 5); - for (int i = 0; i < 20; i++) putchar(m[i]); - munmap(m, 9000); - printf("\n*\n"); - return 0; - } - ''' - self.do_run(src, '*\ndata from the file .\nfrom the file ......\n*\n') - - def test_cubescript(self): - if self.emcc_args is None: return self.skip('requires emcc') - if self.run_name == 'o2': - self.emcc_args += ['--closure', '1'] # Use closure here for some additional coverage - - Building.COMPILER_TEST_OPTS = filter(lambda x: x != '-g', Building.COMPILER_TEST_OPTS) # remove -g, so we have one test without it by default - if self.emcc_args is None: Settings.SAFE_HEAP = 0 # Has some actual loads of unwritten-to places, in the C++ code... - - # Overflows happen in hash loop - Settings.CORRECT_OVERFLOWS = 1 - Settings.CHECK_OVERFLOWS = 0 - - if Settings.USE_TYPED_ARRAYS == 2: - Settings.CORRECT_SIGNS = 1 - - self.do_run(path_from_root('tests', 'cubescript'), '*\nTemp is 33\n9\n5\nhello, everyone\n*', main_file='command.cpp') - - assert 'asm2g' in test_modes - if self.run_name == 'asm2g': - results = {} - original = open('src.cpp.o.js').read() - results[Settings.ALIASING_FUNCTION_POINTERS] = len(original) - Settings.ALIASING_FUNCTION_POINTERS = 1 - Settings.ALIASING_FUNCTION_POINTERS - self.do_run(path_from_root('tests', 'cubescript'), '*\nTemp is 33\n9\n5\nhello, everyone\n*', main_file='command.cpp') - final = open('src.cpp.o.js').read() - results[Settings.ALIASING_FUNCTION_POINTERS] = len(final) - open('original.js', 'w').write(original) - print results - assert results[1] < 0.99*results[0] - assert ' & 3]()' in original, 'small function table exists' - assert ' & 3]()' not in final, 'small function table does not exist' - assert ' & 255]()' not in original, 'big function table does not exist' - assert ' & 255]()' in final, 'big function table exists' - - def test_gcc_unmangler(self): - Settings.NAMED_GLOBALS = 1 # test coverage for this - - Building.COMPILER_TEST_OPTS += ['-I' + path_from_root('third_party')] - - self.do_run(open(path_from_root('third_party', 'gcc_demangler.c')).read(), '*d_demangle(char const*, int, unsigned int*)*', args=['_ZL10d_demanglePKciPj']) - - #### Code snippet that is helpful to search for nonportable optimizations #### - #global LLVM_OPT_OPTS - #for opt in ['-aa-eval', '-adce', '-always-inline', '-argpromotion', '-basicaa', '-basiccg', '-block-placement', '-break-crit-edges', '-codegenprepare', '-constmerge', '-constprop', '-correlated-propagation', '-count-aa', '-dce', '-deadargelim', '-deadtypeelim', '-debug-aa', '-die', '-domfrontier', '-domtree', '-dse', '-extract-blocks', '-functionattrs', '-globaldce', '-globalopt', '-globalsmodref-aa', '-gvn', '-indvars', '-inline', '-insert-edge-profiling', '-insert-optimal-edge-profiling', '-instcombine', '-instcount', '-instnamer', '-internalize', '-intervals', '-ipconstprop', '-ipsccp', '-iv-users', '-jump-threading', '-lazy-value-info', '-lcssa', '-lda', '-libcall-aa', '-licm', '-lint', '-live-values', '-loop-deletion', '-loop-extract', '-loop-extract-single', '-loop-index-split', '-loop-reduce', '-loop-rotate', '-loop-unroll', '-loop-unswitch', '-loops', '-loopsimplify', '-loweratomic', '-lowerinvoke', '-lowersetjmp', '-lowerswitch', '-mem2reg', '-memcpyopt', '-memdep', '-mergefunc', '-mergereturn', '-module-debuginfo', '-no-aa', '-no-profile', '-partial-inliner', '-partialspecialization', '-pointertracking', '-postdomfrontier', '-postdomtree', '-preverify', '-prune-eh', '-reassociate', '-reg2mem', '-regions', '-scalar-evolution', '-scalarrepl', '-sccp', '-scev-aa', '-simplify-libcalls', '-simplify-libcalls-halfpowr', '-simplifycfg', '-sink', '-split-geps', '-sretpromotion', '-strip', '-strip-dead-debug-info', '-strip-dead-prototypes', '-strip-debug-declare', '-strip-nondebug', '-tailcallelim', '-tailduplicate', '-targetdata', '-tbaa']: - # LLVM_OPT_OPTS = [opt] - # try: - # self.do_run(path_from_root(['third_party']), '*d_demangle(char const*, int, unsigned int*)*', args=['_ZL10d_demanglePKciPj'], main_file='gcc_demangler.c') - # print opt, "ok" - # except: - # print opt, "FAIL" - - def test_lua(self): - if self.emcc_args is None: return self.skip('requires emcc') - if Settings.QUANTUM_SIZE == 1: return self.skip('TODO: make this work') - - self.do_run('', - 'hello lua world!\n17\n1\n2\n3\n4\n7', - args=['-e', '''print("hello lua world!");print(17);for x = 1,4 do print(x) end;print(10-3)'''], - libraries=self.get_library('lua', [os.path.join('src', 'lua'), os.path.join('src', 'liblua.a')], make=['make', 'generic'], configure=None), - includes=[path_from_root('tests', 'lua')], - output_nicerizer=lambda string, err: (string + err).replace('\n\n', '\n').replace('\n\n', '\n')) - - def get_freetype(self): - Settings.DEAD_FUNCTIONS += ['_inflateEnd', '_inflate', '_inflateReset', '_inflateInit2_'] - - return self.get_library('freetype', - os.path.join('objs', '.libs', 'libfreetype.a')) - - def test_freetype(self): - if self.emcc_args is None: return self.skip('requires emcc') - if Settings.QUANTUM_SIZE == 1: return self.skip('TODO: Figure out and try to fix') - if Settings.ASM_JS and '-O2' not in self.emcc_args: return self.skip('mozilla bug 863867') - - if Settings.CORRECT_SIGNS == 0: Settings.CORRECT_SIGNS = 1 # Not sure why, but needed - - post = ''' -def process(filename): - import tools.shared as shared - # Embed the font into the document - src = open(filename, 'r').read().replace( - '// {{PRE_RUN_ADDITIONS}}', - "FS.createDataFile('/', 'font.ttf', %s, true, false);" % str( - map(ord, open(shared.path_from_root('tests', 'freetype', 'LiberationSansBold.ttf'), 'rb').read()) - ) - ) - open(filename, 'w').write(src) -''' - - # Not needed for js, but useful for debugging - shutil.copyfile(path_from_root('tests', 'freetype', 'LiberationSansBold.ttf'), os.path.join(self.get_dir(), 'font.ttf')) - - # Main - self.do_run(open(path_from_root('tests', 'freetype', 'main.c'), 'r').read(), - open(path_from_root('tests', 'freetype', 'ref.txt'), 'r').read(), - ['font.ttf', 'test!', '150', '120', '25'], - libraries=self.get_freetype(), - includes=[path_from_root('tests', 'freetype', 'include')], - post_build=post) - #build_ll_hook=self.do_autodebug) - - # github issue 324 - print '[issue 324]' - self.do_run(open(path_from_root('tests', 'freetype', 'main_2.c'), 'r').read(), - open(path_from_root('tests', 'freetype', 'ref_2.txt'), 'r').read(), - ['font.ttf', 'w', '32', '32', '25'], - libraries=self.get_freetype(), - includes=[path_from_root('tests', 'freetype', 'include')], - post_build=post) - - print '[issue 324 case 2]' - self.do_run(open(path_from_root('tests', 'freetype', 'main_3.c'), 'r').read(), - open(path_from_root('tests', 'freetype', 'ref_3.txt'), 'r').read(), - ['font.ttf', 'W', '32', '32', '0'], - libraries=self.get_freetype(), - includes=[path_from_root('tests', 'freetype', 'include')], - post_build=post) - - print '[issue 324 case 3]' - self.do_run('', - open(path_from_root('tests', 'freetype', 'ref_4.txt'), 'r').read(), - ['font.ttf', 'ea', '40', '32', '0'], - no_build=True) - - def test_sqlite(self): - # gcc -O3 -I/home/alon/Dev/emscripten/tests/sqlite -ldl src.c - if self.emcc_args is None: return self.skip('Very slow without ta2, and we would also need to include dlmalloc manually without emcc') - if Settings.QUANTUM_SIZE == 1: return self.skip('TODO FIXME') - self.banned_js_engines = [NODE_JS] # OOM in older node - - Settings.CORRECT_SIGNS = 1 - Settings.CORRECT_OVERFLOWS = 0 - Settings.CORRECT_ROUNDINGS = 0 - if self.emcc_args is None: Settings.SAFE_HEAP = 0 # uses time.h to set random bytes, other stuff - Settings.DISABLE_EXCEPTION_CATCHING = 1 - Settings.FAST_MEMORY = 4*1024*1024 - Settings.EXPORTED_FUNCTIONS += ['_sqlite3_open', '_sqlite3_close', '_sqlite3_exec', '_sqlite3_free', '_callback']; - if Settings.ASM_JS == 1 and '-g' in self.emcc_args: - print "disabling inlining" # without registerize (which -g disables), we generate huge amounts of code - Settings.INLINING_LIMIT = 50 - - self.do_run(r''' - #define SQLITE_DISABLE_LFS - #define LONGDOUBLE_TYPE double - #define SQLITE_INT64_TYPE long long int - #define SQLITE_THREADSAFE 0 - ''' + open(path_from_root('tests', 'sqlite', 'sqlite3.c'), 'r').read() + - open(path_from_root('tests', 'sqlite', 'benchmark.c'), 'r').read(), - open(path_from_root('tests', 'sqlite', 'benchmark.txt'), 'r').read(), - includes=[path_from_root('tests', 'sqlite')], - force_c=True) - - def test_zlib(self): - if not Settings.USE_TYPED_ARRAYS == 2: return self.skip('works in general, but cached build will be optimized and fail, so disable this') - - if Settings.ASM_JS: - self.banned_js_engines = [NODE_JS] # TODO investigate - - if self.emcc_args is not None and '-O2' in self.emcc_args and 'ASM_JS=0' not in self.emcc_args: # without asm, closure minifies Math.imul badly - self.emcc_args += ['--closure', '1'] # Use closure here for some additional coverage - - Settings.CORRECT_SIGNS = 1 - - self.do_run(open(path_from_root('tests', 'zlib', 'example.c'), 'r').read(), - open(path_from_root('tests', 'zlib', 'ref.txt'), 'r').read(), - libraries=self.get_library('zlib', os.path.join('libz.a'), make_args=['libz.a']), - includes=[path_from_root('tests', 'zlib')], - force_c=True) - - def test_the_bullet(self): # Called thus so it runs late in the alphabetical cycle... it is long - if self.emcc_args is None: return self.skip('requires emcc') - if Building.LLVM_OPTS and self.emcc_args is None: Settings.SAFE_HEAP = 0 # Optimizations make it so we do not have debug info on the line we need to ignore - - Settings.DEAD_FUNCTIONS = ['__ZSt9terminatev'] - - # Note: this is also a good test of per-file and per-line changes (since we have multiple files, and correct specific lines) - if Settings.SAFE_HEAP: - # Ignore bitfield warnings - Settings.SAFE_HEAP = 3 - Settings.SAFE_HEAP_LINES = ['btVoronoiSimplexSolver.h:40', 'btVoronoiSimplexSolver.h:41', - 'btVoronoiSimplexSolver.h:42', 'btVoronoiSimplexSolver.h:43'] - - def test(): - self.do_run(open(path_from_root('tests', 'bullet', 'Demos', 'HelloWorld', 'HelloWorld.cpp'), 'r').read(), - [open(path_from_root('tests', 'bullet', 'output.txt'), 'r').read(), # different roundings - open(path_from_root('tests', 'bullet', 'output2.txt'), 'r').read(), - open(path_from_root('tests', 'bullet', 'output3.txt'), 'r').read()], - libraries=self.get_library('bullet', [os.path.join('src', '.libs', 'libBulletDynamics.a'), - os.path.join('src', '.libs', 'libBulletCollision.a'), - os.path.join('src', '.libs', 'libLinearMath.a')], - configure_args=['--disable-demos','--disable-dependency-tracking']), - includes=[path_from_root('tests', 'bullet', 'src')]) - test() - - assert 'asm2g' in test_modes - if self.run_name == 'asm2g': - # Test forced alignment - print >> sys.stderr, 'testing FORCE_ALIGNED_MEMORY' - old = open('src.cpp.o.js').read() - Settings.FORCE_ALIGNED_MEMORY = 1 - test() - new = open('src.cpp.o.js').read() - print len(old), len(new), old.count('tempBigInt'), new.count('tempBigInt') - assert len(old) > len(new) - assert old.count('tempBigInt') > new.count('tempBigInt') - - def test_poppler(self): - if self.emcc_args is None: return self.skip('very slow, we only do this in emcc runs') - - Settings.CORRECT_OVERFLOWS = 1 - Settings.CORRECT_SIGNS = 1 - - Building.COMPILER_TEST_OPTS += [ - '-I' + path_from_root('tests', 'freetype', 'include'), - '-I' + path_from_root('tests', 'poppler', 'include'), - ] - - Settings.INVOKE_RUN = 0 # We append code that does run() ourselves - - # See post(), below - input_file = open(os.path.join(self.get_dir(), 'paper.pdf.js'), 'w') - input_file.write(str(map(ord, open(path_from_root('tests', 'poppler', 'paper.pdf'), 'rb').read()))) - input_file.close() - - post = ''' -def process(filename): - # To avoid loading this large file to memory and altering it, we simply append to the end - src = open(filename, 'a') - src.write( - \'\'\' - FS.createDataFile('/', 'paper.pdf', eval(Module.read('paper.pdf.js')), true, false); - Module.callMain(Module.arguments); - Module.print("Data: " + JSON.stringify(FS.root.contents['filename-1.ppm'].contents.map(function(x) { return unSign(x, 8) }))); - \'\'\' - ) - src.close() -''' - - #fontconfig = self.get_library('fontconfig', [os.path.join('src', '.libs', 'libfontconfig.a')]) # Used in file, but not needed, mostly - - freetype = self.get_freetype() - - poppler = self.get_library('poppler', - [os.path.join('utils', 'pdftoppm.o'), - os.path.join('utils', 'parseargs.o'), - os.path.join('poppler', '.libs', 'libpoppler.a')], - env_init={ 'FONTCONFIG_CFLAGS': ' ', 'FONTCONFIG_LIBS': ' ' }, - configure_args=['--disable-libjpeg', '--disable-libpng', '--disable-poppler-qt', '--disable-poppler-qt4', '--disable-cms', '--disable-cairo-output', '--disable-abiword-output', '--enable-shared=no']) - - # Combine libraries - - combined = os.path.join(self.get_dir(), 'poppler-combined.bc') - Building.link(poppler + freetype, combined) - - self.do_ll_run(combined, - map(ord, open(path_from_root('tests', 'poppler', 'ref.ppm'), 'r').read()).__str__().replace(' ', ''), - args='-scale-to 512 paper.pdf filename'.split(' '), - post_build=post) - #, build_ll_hook=self.do_autodebug) - - def test_openjpeg(self): - if self.emcc_args is None: return self.skip('needs libc for getopt') - - Building.COMPILER_TEST_OPTS = filter(lambda x: x != '-g', Building.COMPILER_TEST_OPTS) # remove -g, so we have one test without it by default - - if Settings.USE_TYPED_ARRAYS == 2: - Settings.CORRECT_SIGNS = 1 - else: - Settings.CORRECT_SIGNS = 2 - Settings.CORRECT_SIGNS_LINES = ["mqc.c:566", "mqc.c:317"] - - post = ''' -def process(filename): - import tools.shared as shared - original_j2k = shared.path_from_root('tests', 'openjpeg', 'syntensity_lobby_s.j2k') - src = open(filename, 'r').read().replace( - '// {{PRE_RUN_ADDITIONS}}', - "FS.createDataFile('/', 'image.j2k', %s, true, false);" % shared.line_splitter(str( - map(ord, open(original_j2k, 'rb').read()) - )) - ).replace( - '// {{POST_RUN_ADDITIONS}}', - "Module.print('Data: ' + JSON.stringify(FS.analyzePath('image.raw').object.contents));" - ) - open(filename, 'w').write(src) -''' - - shutil.copy(path_from_root('tests', 'openjpeg', 'opj_config.h'), self.get_dir()) - - lib = self.get_library('openjpeg', - [os.path.sep.join('codec/CMakeFiles/j2k_to_image.dir/index.c.o'.split('/')), - os.path.sep.join('codec/CMakeFiles/j2k_to_image.dir/convert.c.o'.split('/')), - os.path.sep.join('codec/CMakeFiles/j2k_to_image.dir/__/common/color.c.o'.split('/')), - os.path.join('bin', 'libopenjpeg.so.1.4.0')], - configure=['cmake', '.'], - #configure_args=['--enable-tiff=no', '--enable-jp3d=no', '--enable-png=no'], - make_args=[]) # no -j 2, since parallel builds can fail - - # We use doubles in JS, so we get slightly different values than native code. So we - # check our output by comparing the average pixel difference - def image_compare(output, err): - # Get the image generated by JS, from the JSON.stringify'd array - m = re.search('\[[\d, -]*\]', output) - try: - js_data = eval(m.group(0)) - except AttributeError: - print 'Failed to find proper image output in: ' + output - raise - - js_data = map(lambda x: x if x >= 0 else 256+x, js_data) # Our output may be signed, so unsign it - - # Get the correct output - true_data = open(path_from_root('tests', 'openjpeg', 'syntensity_lobby_s.raw'), 'rb').read() - - # Compare them - assert(len(js_data) == len(true_data)) - num = len(js_data) - diff_total = js_total = true_total = 0 - for i in range(num): - js_total += js_data[i] - true_total += ord(true_data[i]) - diff_total += abs(js_data[i] - ord(true_data[i])) - js_mean = js_total/float(num) - true_mean = true_total/float(num) - diff_mean = diff_total/float(num) - - image_mean = 83.265 - #print '[image stats:', js_mean, image_mean, true_mean, diff_mean, num, ']' - assert abs(js_mean - image_mean) < 0.01 - assert abs(true_mean - image_mean) < 0.01 - assert diff_mean < 0.01 - - return output - - self.emcc_args += ['--minify', '0'] # to compare the versions - - def do_test(): - self.do_run(open(path_from_root('tests', 'openjpeg', 'codec', 'j2k_to_image.c'), 'r').read(), - 'Successfully generated', # The real test for valid output is in image_compare - '-i image.j2k -o image.raw'.split(' '), - libraries=lib, - includes=[path_from_root('tests', 'openjpeg', 'libopenjpeg'), - path_from_root('tests', 'openjpeg', 'codec'), - path_from_root('tests', 'openjpeg', 'common'), - os.path.join(self.get_build_dir(), 'openjpeg')], - force_c=True, - post_build=post, - output_nicerizer=image_compare)#, build_ll_hook=self.do_autodebug) - - do_test() - - # some test coverage for EMCC_DEBUG 1 and 2 - if self.emcc_args and '-O2' in self.emcc_args and 'EMCC_DEBUG' not in os.environ: - shutil.copyfile('src.c.o.js', 'release.js') - try: - os.environ['EMCC_DEBUG'] = '1' - print '2' - do_test() - shutil.copyfile('src.c.o.js', 'debug1.js') - os.environ['EMCC_DEBUG'] = '2' - print '3' - do_test() - shutil.copyfile('src.c.o.js', 'debug2.js') - finally: - del os.environ['EMCC_DEBUG'] - for debug in [1,2]: - def clean(text): - return text.replace('\n\n', '\n').replace('\n\n', '\n').replace('\n\n', '\n').replace('\n\n', '\n').replace('\n\n', '\n').replace('{\n}', '{}') - self.assertIdentical(clean(open('release.js').read()), clean(open('debug%d.js' % debug).read())) # EMCC_DEBUG=1 mode must not generate different code! - print >> sys.stderr, 'debug check %d passed too' % debug - - try: - os.environ['EMCC_FORCE_STDLIBS'] = '1' - print 'EMCC_FORCE_STDLIBS' - do_test() - finally: - del os.environ['EMCC_FORCE_STDLIBS'] - print >> sys.stderr, 'EMCC_FORCE_STDLIBS ok' - - try_delete(CANONICAL_TEMP_DIR) - else: - print >> sys.stderr, 'not doing debug check' - - def test_python(self): - if self.emcc_args is None: return self.skip('requires emcc') - if Settings.QUANTUM_SIZE == 1: return self.skip('TODO: make this work') - if not self.is_le32(): return self.skip('fails on non-le32') # FIXME - - #Settings.EXPORTED_FUNCTIONS += ['_PyRun_SimpleStringFlags'] # for the demo - - if self.is_le32(): - bitcode = path_from_root('tests', 'python', 'python.le32.bc') - else: - bitcode = path_from_root('tests', 'python', 'python.small.bc') - - self.do_ll_run(bitcode, - 'hello python world!\n[0, 2, 4, 6]\n5\n22\n5.470000', - args=['-S', '-c' '''print "hello python world!"; print [x*2 for x in range(4)]; t=2; print 10-3-t; print (lambda x: x*2)(11); print '%f' % 5.47''']) - - def test_lifetime(self): - if self.emcc_args is None: return self.skip('test relies on emcc opts') - - self.do_ll_run(path_from_root('tests', 'lifetime.ll'), 'hello, world!\n') - if '-O1' in self.emcc_args or '-O2' in self.emcc_args: - assert 'a18' not in open(os.path.join(self.get_dir(), 'src.cpp.o.js')).read(), 'lifetime stuff and their vars must be culled' - - # Test cases in separate files. Note that these files may contain invalid .ll! - # They are only valid enough for us to read for test purposes, not for llvm-as - # to process. - def test_cases(self): - if Building.LLVM_OPTS: return self.skip("Our code is not exactly 'normal' llvm assembly") - - try: - os.environ['EMCC_LEAVE_INPUTS_RAW'] = '1' - Settings.CHECK_OVERFLOWS = 0 - - for name in glob.glob(path_from_root('tests', 'cases', '*.ll')): - shortname = name.replace('.ll', '') - if '' not in shortname: continue - if '_ta2' in shortname and not Settings.USE_TYPED_ARRAYS == 2: - print self.skip('case "%s" only relevant for ta2' % shortname) - continue - if '_noasm' in shortname and Settings.ASM_JS: - print self.skip('case "%s" not relevant for asm.js' % shortname) - continue - print >> sys.stderr, "Testing case '%s'..." % shortname - output_file = path_from_root('tests', 'cases', shortname + '.txt') - if Settings.QUANTUM_SIZE == 1: - q1_output_file = path_from_root('tests', 'cases', shortname + '_q1.txt') - if os.path.exists(q1_output_file): - output_file = q1_output_file - if os.path.exists(output_file): - output = open(output_file, 'r').read() - else: - output = 'hello, world!' - if output.rstrip() != 'skip': - self.do_ll_run(path_from_root('tests', 'cases', name), output) - # Optional source checking, a python script that gets a global generated with the source - src_checker = path_from_root('tests', 'cases', shortname + '.py') - if os.path.exists(src_checker): - generated = open('src.cpp.o.js').read() - exec(open(src_checker).read()) - - finally: - del os.environ['EMCC_LEAVE_INPUTS_RAW'] - - def test_fuzz(self): - if Settings.USE_TYPED_ARRAYS != 2: return self.skip('needs ta2') - - Building.COMPILER_TEST_OPTS += ['-I' + path_from_root('tests', 'fuzz')] - - def run_all(x): - print x - for name in glob.glob(path_from_root('tests', 'fuzz', '*.c')): - print name - self.do_run(open(path_from_root('tests', 'fuzz', name)).read(), - open(path_from_root('tests', 'fuzz', name + '.txt')).read(), force_c=True) - - run_all('normal') - - self.emcc_args += ['--llvm-lto', '1'] - - run_all('lto') - - # Autodebug the code - def do_autodebug(self, filename): - output = Popen([PYTHON, AUTODEBUGGER, filename+'.o.ll', filename+'.o.ll.ll'], stdout=PIPE, stderr=self.stderr_redirect).communicate()[0] - assert 'Success.' in output, output - self.prep_ll_run(filename, filename+'.o.ll.ll', force_recompile=True) # rebuild .bc # TODO: use code in do_autodebug_post for this - - # Autodebug the code, after LLVM opts. Will only work once! - def do_autodebug_post(self, filename): - if not hasattr(self, 'post'): - print 'Asking for post re-call' - self.post = True - return True - print 'Autodebugging during post time' - delattr(self, 'post') - output = Popen([PYTHON, AUTODEBUGGER, filename+'.o.ll', filename+'.o.ll.ll'], stdout=PIPE, stderr=self.stderr_redirect).communicate()[0] - assert 'Success.' in output, output - shutil.copyfile(filename + '.o.ll.ll', filename + '.o.ll') - Building.llvm_as(filename) - Building.llvm_dis(filename) - - def test_autodebug(self): - if Building.LLVM_OPTS: return self.skip('LLVM opts mess us up') - Building.COMPILER_TEST_OPTS += ['--llvm-opts', '0'] - - # Run a test that should work, generating some code - self.test_structs() - - filename = os.path.join(self.get_dir(), 'src.cpp') - self.do_autodebug(filename) - - # Compare to each other, and to expected output - self.do_ll_run(path_from_root('tests', filename+'.o.ll.ll'), '''AD:-1,1''') - assert open('stdout').read().startswith('AD:-1'), 'We must note when we enter functions' - - # Test using build_ll_hook - src = ''' - #include - - char cache[256], *next = cache; - - int main() - { - cache[10] = 25; - next[20] = 51; - int x = cache[10]; - double y = 11.52; - printf("*%d,%d,%.2f*\\n", x, cache[20], y); - return 0; - } - ''' - self.do_run(src, '''AD:-1,1''', build_ll_hook=self.do_autodebug) - - def test_corruption(self): - if Settings.ASM_JS: return self.skip('cannot use corruption checks in asm') - if Settings.USE_TYPED_ARRAYS != 2: return self.skip('needs ta2 for actual test') - - Settings.CORRUPTION_CHECK = 1 - - src = r''' - #include - #include - #include - int main(int argc, char **argv) { - int size = 1024*argc; - char *buffer = (char*)malloc(size); - #if CORRUPT - memset(buffer, argc, size+15); - #else - memset(buffer, argc, size); - #endif - for (int x = 0; x < size; x += argc*3) buffer[x] = x/3; - int ret = 0; - for (int x = 0; x < size; x++) ret += buffer[x]; - free(buffer); - printf("All ok, %d\n", ret); - } - ''' - - for corrupt in [1]: - self.do_run(src.replace('CORRUPT', str(corrupt)), 'Heap corruption detected!' if corrupt else 'All ok, 4209') - - def test_corruption_2(self): - if Settings.ASM_JS: return self.skip('cannot use corruption checks in asm') - if Settings.USE_TYPED_ARRAYS != 2: return self.skip('needs ta2 for actual test') - - Settings.SAFE_HEAP = 1 - Settings.CORRUPTION_CHECK = 1 - - # test for free(0), malloc(0), etc. - src = r''' - #include - #include - #include - #include - - void bye() { - printf("all ok\n"); - } - - int main() { - atexit(bye); - - std::string testPath = "/Script/WA-KA.txt"; - std::fstream str(testPath.c_str(), std::ios::in | std::ios::binary); - - if (str.is_open()) - { - std::cout << "open!" << std::endl; - } else { - std::cout << "missing!" << std::endl; - } - - return 1; - } - ''' - self.do_run(src, 'missing!\nall ok\n') - - def test_corruption_3(self): - if Settings.ASM_JS: return self.skip('cannot use corruption checks in asm') - if Settings.USE_TYPED_ARRAYS != 2: return self.skip('needs ta2 for actual test') - - Settings.CORRUPTION_CHECK = 1 - - # realloc - src = r''' - #include - #include - #include - - void bye() { - printf("all ok\n"); - } - - int main(int argc, char **argv) { - atexit(bye); - - char *buffer = (char*)malloc(100); - for (int i = 0; i < 100; i++) buffer[i] = (i*i)%256; - buffer = (char*)realloc(buffer, argc + 50); - for (int i = 0; i < argc + 50; i++) { - //printf("%d : %d : %d : %d\n", i, (int)(buffer + i), buffer[i], (char)((i*i)%256)); - assert(buffer[i] == (char)((i*i)%256)); - } - return 1; - } - ''' - self.do_run(src, 'all ok\n') - - ### Integration tests - - def test_ccall(self): - 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 - - src = r''' - #include - #include - - extern "C" { - int get_int() { return 5; } - float get_float() { return 3.14; } - char * get_string() { return "hello world"; } - void print_int(int x) { printf("%d\n", x); } - void print_float(float x) { printf("%.2f\n", x); } - void print_string(char *x) { printf("%s\n", x); } - int multi(int x, float y, int z, char *str) { if (x) puts(str); return (x+y)*z; } - int * pointer(int *in) { printf("%d\n", *in); static int ret = 21; return &ret; } - } - - int main(int argc, char **argv) { - return 0; - } - ''' - - post = ''' -def process(filename): - src = \'\'\' - var Module = { 'noInitialRun': true }; - \'\'\' + open(filename, 'r').read() + \'\'\' - Module.addOnExit(function () { - Module.print('*'); - var ret; - ret = Module['ccall']('get_int', 'number'); Module.print([typeof ret, ret]); - ret = ccall('get_float', 'number'); Module.print([typeof ret, ret.toFixed(2)]); - ret = ccall('get_string', 'string'); Module.print([typeof ret, ret]); - ret = ccall('print_int', null, ['number'], [12]); Module.print(typeof ret); - ret = ccall('print_float', null, ['number'], [14.56]); Module.print(typeof ret); - ret = ccall('print_string', null, ['string'], ["cheez"]); Module.print(typeof ret); - ret = ccall('print_string', null, ['array'], [[97, 114, 114, 45, 97, 121, 0]]); Module.print(typeof ret); - ret = ccall('multi', 'number', ['number', 'number', 'number', 'string'], [2, 1.4, 3, 'more']); Module.print([typeof ret, ret]); - var p = ccall('malloc', 'pointer', ['number'], [4]); - setValue(p, 650, 'i32'); - ret = ccall('pointer', 'pointer', ['pointer'], [p]); Module.print([typeof ret, getValue(ret, 'i32')]); - Module.print('*'); - // part 2: cwrap - var multi = Module['cwrap']('multi', 'number', ['number', 'number', 'number', 'string']); - Module.print(multi(2, 1.4, 3, 'atr')); - Module.print(multi(8, 5.4, 4, 'bret')); - Module.print('*'); - // part 3: avoid stack explosion - for (var i = 0; i < TOTAL_STACK/60; i++) { - ccall('multi', 'number', ['number', 'number', 'number', 'string'], [0, 0, 0, '123456789012345678901234567890123456789012345678901234567890']); - } - Module.print('stack is ok.'); - }); - Module.callMain(); - \'\'\' - open(filename, 'w').write(src) -''' - - Settings.EXPORTED_FUNCTIONS += ['_get_int', '_get_float', '_get_string', '_print_int', '_print_float', '_print_string', '_multi', '_pointer', '_malloc'] - - self.do_run(src, '*\nnumber,5\nnumber,3.14\nstring,hello world\n12\nundefined\n14.56\nundefined\ncheez\nundefined\narr-ay\nundefined\nmore\nnumber,10\n650\nnumber,21\n*\natr\n10\nbret\n53\n*\nstack is ok.\n', post_build=post) - - def test_pgo(self): - if Settings.ASM_JS: return self.skip('PGO does not work in asm mode') - - def run_all(name, src): - print name - def test(expected, args=[], no_build=False): - self.do_run(src, expected, args=args, no_build=no_build) - return open(self.in_dir('src.cpp.o.js')).read() - - # Sanity check that it works and the dead function is emitted - js = test('*9*') - assert 'function _unused(' in js - - # Run with PGO, see that unused is true to its name - Settings.PGO = 1 - test("*9*\n-s DEAD_FUNCTIONS='[\"_unused\"]'") - Settings.PGO = 0 - - # Kill off the dead function, still works and it is not emitted - Settings.DEAD_FUNCTIONS = ['_unused'] - js = test('*9*') - assert 'function _unused($' not in js # no compiled code - assert 'function _unused(' in js # lib-generated stub - Settings.DEAD_FUNCTIONS = [] - - # Run the same code with argc that uses the dead function, see abort - test(('missing function: unused'), args=['a', 'b'], no_build=True) - - # Normal stuff - run_all('normal', r''' - #include - extern "C" { - int used(int x) { - if (x == 0) return -1; - return used(x/3) + used(x/17) + x%5; - } - int unused(int x) { - if (x == 0) return -1; - return unused(x/4) + unused(x/23) + x%7; - } - } - int main(int argc, char **argv) { - printf("*%d*\n", argc == 3 ? unused(argv[0][0] + 1024) : used(argc + 1555)); - return 0; - } - ''') - - # Call by function pointer - run_all('function pointers', r''' - #include - extern "C" { - int used(int x) { - if (x == 0) return -1; - return used(x/3) + used(x/17) + x%5; - } - int unused(int x) { - if (x == 0) return -1; - return unused(x/4) + unused(x/23) + x%7; - } - } - typedef int (*ii)(int); - int main(int argc, char **argv) { - ii pointers[256]; - for (int i = 0; i < 256; i++) { - pointers[i] = (i == 3) ? unused : used; - } - printf("*%d*\n", pointers[argc](argc + 1555)); - return 0; - } - ''') - - def test_asm_pgo(self): - if not Settings.ASM_JS: return self.skip('this is a test for PGO for asm (NB: not *in* asm)') - - src = open(path_from_root('tests', 'hello_libcxx.cpp')).read() - output = 'hello, world!' - - self.do_run(src, output) - shutil.move(self.in_dir('src.cpp.o.js'), self.in_dir('normal.js')) - - Settings.ASM_JS = 0 - Settings.PGO = 1 - self.do_run(src, output) - Settings.ASM_JS = 1 - Settings.PGO = 0 - - shutil.move(self.in_dir('src.cpp.o.js'), self.in_dir('pgo.js')) - pgo_output = run_js(self.in_dir('pgo.js')).split('\n')[1] - open('pgo_data.rsp', 'w').write(pgo_output) - - # with response file - - self.emcc_args += ['@pgo_data.rsp'] - self.do_run(src, output) - self.emcc_args.pop() - shutil.move(self.in_dir('src.cpp.o.js'), self.in_dir('pgoed.js')) - - before = len(open('normal.js').read()) - after = len(open('pgoed.js').read()) - assert after < 0.90 * before, [before, after] # expect a size reduction - - # with response in settings element itself - - open('dead_funcs', 'w').write(pgo_output[pgo_output.find('['):-1]) - self.emcc_args += ['-s', 'DEAD_FUNCTIONS=@' + self.in_dir('dead_funcs')] - self.do_run(src, output) - self.emcc_args.pop() - self.emcc_args.pop() - shutil.move(self.in_dir('src.cpp.o.js'), self.in_dir('pgoed2.js')) - assert open('pgoed.js').read() == open('pgoed2.js').read() - - # with relative response in settings element itself - - open('dead_funcs', 'w').write(pgo_output[pgo_output.find('['):-1]) - self.emcc_args += ['-s', 'DEAD_FUNCTIONS=@dead_funcs'] - self.do_run(src, output) - self.emcc_args.pop() - self.emcc_args.pop() - shutil.move(self.in_dir('src.cpp.o.js'), self.in_dir('pgoed2.js')) - assert open('pgoed.js').read() == open('pgoed2.js').read() - - def test_exported_response(self): - if self.emcc_args is None: return self.skip('requires emcc') - - src = r''' - #include - #include - - extern "C" { - int other_function() { return 5; } - } - - int main() { - printf("waka!\n"); - return 0; - } - ''' - open('exps', 'w').write('["_main","_other_function"]') - - self.emcc_args += ['-s', 'EXPORTED_FUNCTIONS=@exps'] - self.do_run(src, '''waka!''') - assert 'other_function' in open('src.cpp.o.js').read() - - def test_add_function(self): - if self.emcc_args is None: return self.skip('requires emcc') - - Settings.INVOKE_RUN = 0 - Settings.RESERVED_FUNCTION_POINTERS = 1 - - src = r''' - #include - #include - - int main(int argc, char **argv) { - int fp = atoi(argv[1]); - printf("fp: %d\n", fp); - void (*f)(int) = reinterpret_cast(fp); - f(7); - return 0; - } - ''' - - open(os.path.join(self.get_dir(), 'post.js'), 'w').write(''' - var newFuncPtr = Runtime.addFunction(function(num) { - Module.print('Hello ' + num + ' from JS!'); - }); - Module.callMain([newFuncPtr.toString()]); - ''') - - self.emcc_args += ['--post-js', 'post.js'] - self.do_run(src, '''Hello 7 from JS!''') - - if Settings.ASM_JS: - Settings.RESERVED_FUNCTION_POINTERS = 0 - self.do_run(src, '''Finished up all reserved function pointers. Use a higher value for RESERVED_FUNCTION_POINTERS.''') - generated = open('src.cpp.o.js').read() - assert 'jsCall' not in generated - Settings.RESERVED_FUNCTION_POINTERS = 1 - - Settings.ALIASING_FUNCTION_POINTERS = 1 - Settings.ALIASING_FUNCTION_POINTERS # flip the test - self.do_run(src, '''Hello 7 from JS!''') - - def test_embind(self): - if self.emcc_args is None: return self.skip('requires emcc') - Building.COMPILER_TEST_OPTS += ['--bind'] - - src = r''' - #include - #include - - using namespace emscripten; - - int main() { - val Math = val::global("Math"); - - // two ways to call Math.abs - printf("abs(-10): %d\n", Math.call("abs", -10)); - printf("abs(-11): %d\n", Math["abs"](-11).as()); - - return 0; - } - ''' - self.do_run(src, 'abs(-10): 10\nabs(-11): 11'); - - def test_embind_2(self): - if self.emcc_args is None: return self.skip('requires emcc') - Building.COMPILER_TEST_OPTS += ['--bind', '--post-js', 'post.js'] - open('post.js', 'w').write(''' - Module.print('lerp ' + Module.lerp(1, 2, 0.66) + '.'); - ''') - src = r''' - #include - #include - #include - using namespace emscripten; - float lerp(float a, float b, float t) { - return (1 - t) * a + t * b; - } - EMSCRIPTEN_BINDINGS(my_module) { - function("lerp", &lerp); - } - ''' - self.do_run(src, 'lerp 1.66'); - - def test_scriptaclass(self): - if self.emcc_args is None: return self.skip('requires emcc') - - Settings.EXPORT_BINDINGS = 1 - - header_filename = os.path.join(self.get_dir(), 'header.h') - header = ''' - struct ScriptMe { - int value; - ScriptMe(int val); - int getVal(); // XXX Sadly, inlining these will result in LLVM not - // producing any code for them (when just building - // as a library) - void mulVal(int mul); - }; - ''' - h = open(header_filename, 'w') - h.write(header) - h.close() - - src = ''' - #include "header.h" - - ScriptMe::ScriptMe(int val) : value(val) { } - int ScriptMe::getVal() { return value; } - void ScriptMe::mulVal(int mul) { value *= mul; } - ''' - - # Way 1: use demangler and namespacer - - script_src = ''' - var sme = Module._.ScriptMe.__new__(83); // malloc(sizeof(ScriptMe)), ScriptMe::ScriptMe(sme, 83) / new ScriptMe(83) (at addr sme) - Module._.ScriptMe.mulVal(sme, 2); // ScriptMe::mulVal(sme, 2) sme.mulVal(2) - Module.print('*' + Module._.ScriptMe.getVal(sme) + '*'); - _free(sme); - Module.print('*ok*'); - ''' - post = ''' -def process(filename): - Popen([PYTHON, DEMANGLER, filename], stdout=open(filename + '.tmp', 'w')).communicate() - Popen([PYTHON, NAMESPACER, filename, filename + '.tmp'], stdout=open(filename + '.tmp2', 'w')).communicate() - src = open(filename, 'r').read().replace( - '// {{MODULE_ADDITIONS}', - 'Module["_"] = ' + open(filename + '.tmp2', 'r').read().replace('var ModuleNames = ', '').rstrip() + ';\n\n' + script_src + '\n\n' + - '// {{MODULE_ADDITIONS}' - ) - open(filename, 'w').write(src) -''' - # 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 and 'ASM_JS=0' not in self.emcc_args: # without asm, closure minifies Math.imul badly - self.emcc_args += ['--closure', '1'] # Use closure here, to test we export things right - - # Way 2: use CppHeaderParser - - Settings.RUNTIME_TYPE_INFO = 1 - - header = ''' - #include - - class Parent { - protected: - int value; - public: - Parent(int val); - Parent(Parent *p, Parent *q); // overload constructor - int getVal() { return value; }; // inline should work just fine here, unlike Way 1 before - void mulVal(int mul); - }; - - class Child1 : public Parent { - public: - Child1() : Parent(7) { printf("Child1:%d\\n", value); }; - Child1(int val) : Parent(val*2) { value -= 1; printf("Child1:%d\\n", value); }; - int getValSqr() { return value*value; } - int getValSqr(int more) { return value*value*more; } - int getValTimes(int times=1) { return value*times; } - }; - - class Child2 : public Parent { - public: - Child2() : Parent(9) { printf("Child2:%d\\n", value); }; - int getValCube() { return value*value*value; } - static void printStatic() { printf("*static*\\n"); } - - virtual void virtualFunc() { printf("*virtualf*\\n"); } - virtual void virtualFunc2() { printf("*virtualf2*\\n"); } - static void runVirtualFunc(Child2 *self) { self->virtualFunc(); }; - private: - void doSomethingSecret() { printf("security breached!\\n"); }; // we should not be able to do this - }; - ''' - open(header_filename, 'w').write(header) - - basename = os.path.join(self.get_dir(), 'bindingtest') - output = Popen([PYTHON, 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" - - Parent::Parent(int val) : value(val) { printf("Parent:%d\\n", val); } - Parent::Parent(Parent *p, Parent *q) : value(p->value + q->value) { printf("Parent:%d\\n", value); } - void Parent::mulVal(int mul) { value *= mul; } - - #include "bindingtest.cpp" - ''' - - post2 = ''' -def process(filename): - src = open(filename, 'a') - src.write(open('bindingtest.js').read() + '\\n\\n') - src.close() -''' - - def post3(filename): - script_src_2 = ''' - var sme = new Module.Parent(42); - sme.mulVal(2); - Module.print('*') - Module.print(sme.getVal()); - - Module.print('c1'); - - var c1 = new Module.Child1(); - Module.print(c1.getVal()); - c1.mulVal(2); - Module.print(c1.getVal()); - Module.print(c1.getValSqr()); - Module.print(c1.getValSqr(3)); - Module.print(c1.getValTimes()); // default argument should be 1 - Module.print(c1.getValTimes(2)); - - Module.print('c1 v2'); - - c1 = new Module.Child1(8); // now with a parameter, we should handle the overloading automatically and properly and use constructor #2 - Module.print(c1.getVal()); - c1.mulVal(2); - Module.print(c1.getVal()); - Module.print(c1.getValSqr()); - Module.print(c1.getValSqr(3)); - - Module.print('c2') - - var c2 = new Module.Child2(); - Module.print(c2.getVal()); - c2.mulVal(2); - Module.print(c2.getVal()); - Module.print(c2.getValCube()); - var succeeded; - try { - succeeded = 0; - Module.print(c2.doSomethingSecret()); // should fail since private - succeeded = 1; - } catch(e) {} - Module.print(succeeded); - try { - succeeded = 0; - Module.print(c2.getValSqr()); // function from the other class - succeeded = 1; - } catch(e) {} - Module.print(succeeded); - try { - succeeded = 0; - c2.getValCube(); // sanity - succeeded = 1; - } catch(e) {} - Module.print(succeeded); - - Module.Child2.prototype.printStatic(); // static calls go through the prototype - - // virtual function - c2.virtualFunc(); - Module.Child2.prototype.runVirtualFunc(c2); - c2.virtualFunc2(); - - // extend the class from JS - var c3 = new Module.Child2; - Module.customizeVTable(c3, [{ - original: Module.Child2.prototype.virtualFunc, - replacement: function() { - Module.print('*js virtualf replacement*'); - } - }, { - original: Module.Child2.prototype.virtualFunc2, - replacement: function() { - Module.print('*js virtualf2 replacement*'); - } - }]); - c3.virtualFunc(); - Module.Child2.prototype.runVirtualFunc(c3); - c3.virtualFunc2(); - - c2.virtualFunc(); // original should remain the same - Module.Child2.prototype.runVirtualFunc(c2); - c2.virtualFunc2(); - Module.print('*ok*'); - ''' - code = open(filename).read() - src = open(filename, 'w') - src.write('var Module = {};\n') # name Module - src.write(code) - src.write(script_src_2 + '\n') - src.close() - - Settings.RESERVED_FUNCTION_POINTERS = 20 - - self.do_run(src, '''* -84 -c1 -Parent:7 -Child1:7 -7 -14 -196 -588 -14 -28 -c1 v2 -Parent:16 -Child1:15 -15 -30 -900 -2700 -c2 -Parent:9 -Child2:9 -9 -18 -5832 -0 -0 -1 -*static* -*virtualf* -*virtualf* -*virtualf2*''' + (''' -Parent:9 -Child2:9 -*js virtualf replacement* -*js virtualf replacement* -*js virtualf2 replacement* -*virtualf* -*virtualf* -*virtualf2*''') + ''' -*ok* -''', post_build=(post2, post3)) - - def test_scriptaclass_2(self): - if self.emcc_args is None: return self.skip('requires emcc') - - Settings.EXPORT_BINDINGS = 1 - - header_filename = os.path.join(self.get_dir(), 'header.h') - header = ''' - #include - #include - - 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([PYTHON, 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') - - Settings.RUNTIME_TYPE_INFO = 1 - if Settings.QUANTUM_SIZE != 4: return self.skip('We assume normal sizes in the output here') - - src = ''' - #include - struct UserStruct { - int x; - char y; - short z; - }; - struct Encloser { - short x; - UserStruct us; - int y; - }; - int main() { - Encloser e; - e.us.y = 5; - printf("*ok:%d*\\n", e.us.y); - return 0; - } - ''' - - post = ''' -def process(filename): - src = open(filename, 'r').read().replace( - '// {{POST_RUN_ADDITIONS}}', - \'\'\' - if (Runtime.typeInfo) { - Module.print('|' + Runtime.typeInfo.UserStruct.fields + '|' + Runtime.typeInfo.UserStruct.flatIndexes + '|'); - var t = Runtime.generateStructInfo(['x', { us: ['x', 'y', 'z'] }, 'y'], 'Encloser') - Module.print('|' + [t.x, t.us.x, t.us.y, t.us.z, t.y] + '|'); - Module.print('|' + JSON.stringify(Runtime.generateStructInfo(['x', 'y', 'z'], 'UserStruct')) + '|'); - } else { - Module.print('No type info.'); - } - \'\'\' - ) - open(filename, 'w').write(src) -''' - - self.do_run(src, - '*ok:5*\n|i32,i8,i16|0,4,6|\n|0,4,8,10,12|\n|{"__size__":8,"x":0,"y":4,"z":6}|', - post_build=post) - - # Make sure that without the setting, we don't spam the .js with the type info - Settings.RUNTIME_TYPE_INFO = 0 - self.do_run(src, 'No type info.', post_build=post) - - ### Tests for tools - - def test_safe_heap(self): - if not Settings.SAFE_HEAP: return self.skip('We need SAFE_HEAP to test SAFE_HEAP') - if Settings.USE_TYPED_ARRAYS == 2: return self.skip('It is ok to violate the load-store assumption with TA2') - if Building.LLVM_OPTS: return self.skip('LLVM can optimize away the intermediate |x|') - - src = ''' - #include - #include - int main() { int *x = (int*)malloc(sizeof(int)); - *x = 20; - float *y = (float*)x; - printf("%f\\n", *y); - printf("*ok*\\n"); - return 0; - } - ''' - - try: - self.do_run(src, '*nothingatall*') - except Exception, e: - # This test *should* fail, by throwing this exception - assert 'Assertion failed: Load-store consistency assumption failure!' in str(e), str(e) - - # And we should not fail if we disable checking on that line - - Settings.SAFE_HEAP = 3 - Settings.SAFE_HEAP_LINES = ["src.cpp:7"] - - self.do_run(src, '*ok*') - - # But if we disable the wrong lines, we still fail - - Settings.SAFE_HEAP_LINES = ["src.cpp:99"] - - try: - self.do_run(src, '*nothingatall*') - except Exception, e: - # This test *should* fail, by throwing this exception - assert 'Assertion failed: Load-store consistency assumption failure!' in str(e), str(e) - - # And reverse the checks with = 2 - - Settings.SAFE_HEAP = 2 - Settings.SAFE_HEAP_LINES = ["src.cpp:99"] - - self.do_run(src, '*ok*') - - Settings.SAFE_HEAP = 1 - - # Linking multiple files should work too - - module = ''' - #include - #include - void callFunc() { int *x = (int*)malloc(sizeof(int)); - *x = 20; - float *y = (float*)x; - printf("%f\\n", *y); - } - ''' - module_name = os.path.join(self.get_dir(), 'module.cpp') - open(module_name, 'w').write(module) - - main = ''' - #include - #include - extern void callFunc(); - int main() { callFunc(); - int *x = (int*)malloc(sizeof(int)); - *x = 20; - float *y = (float*)x; - printf("%f\\n", *y); - printf("*ok*\\n"); - return 0; - } - ''' - main_name = os.path.join(self.get_dir(), 'main.cpp') - open(main_name, 'w').write(main) - - Building.emcc(module_name, ['-g']) - Building.emcc(main_name, ['-g']) - all_name = os.path.join(self.get_dir(), 'all.bc') - Building.link([module_name + '.o', main_name + '.o'], all_name) - - try: - self.do_ll_run(all_name, '*nothingatall*') - except Exception, e: - # This test *should* fail, by throwing this exception - assert 'Assertion failed: Load-store consistency assumption failure!' in str(e), str(e) - - # And we should not fail if we disable checking on those lines - - Settings.SAFE_HEAP = 3 - Settings.SAFE_HEAP_LINES = ["module.cpp:7", "main.cpp:9"] - - self.do_ll_run(all_name, '*ok*') - - # But we will fail if we do not disable exactly what we need to - any mistake leads to error - - for lines in [["module.cpp:22", "main.cpp:9"], ["module.cpp:7", "main.cpp:29"], ["module.cpp:127", "main.cpp:449"], ["module.cpp:7"], ["main.cpp:9"]]: - Settings.SAFE_HEAP_LINES = lines - try: - self.do_ll_run(all_name, '*nothingatall*') - except Exception, e: - # This test *should* fail, by throwing this exception - assert 'Assertion failed: Load-store consistency assumption failure!' in str(e), str(e) - - def test_debug(self): - if '-g' not in Building.COMPILER_TEST_OPTS: Building.COMPILER_TEST_OPTS.append('-g') - if self.emcc_args is not None: - if '-O1' in self.emcc_args or '-O2' in self.emcc_args: return self.skip('optimizations remove LLVM debug info') - - src = ''' - #include - #include - - void checker(int x) { - x += 20; - assert(x < 15); // this is line 7! - } - - int main() { - checker(10); - return 0; - } - ''' - try: - self.do_run(src, '*nothingatall*') - except Exception, e: - # This test *should* fail - assert 'Assertion failed: x < 15' in str(e), str(e) - - lines = open('src.cpp.o.js', 'r').readlines() - lines = filter(lambda line: '___assert_fail(' in line or '___assert_func(' in line, lines) - found_line_num = any(('//@line 7 "' in line) for line in lines) - found_filename = any(('src.cpp"\n' in line) for line in lines) - assert found_line_num, 'Must have debug info with the line number' - assert found_filename, 'Must have debug info with the filename' - - def test_source_map(self): - if Settings.USE_TYPED_ARRAYS != 2: return self.skip("doesn't pass without typed arrays") - if NODE_JS not in JS_ENGINES: return self.skip('sourcemapper requires Node to run') - if '-g' not in Building.COMPILER_TEST_OPTS: Building.COMPILER_TEST_OPTS.append('-g') - - src = ''' - #include - #include - - __attribute__((noinline)) int foo() { - printf("hi"); // line 6 - return 1; // line 7 - } - - int main() { - printf("%d", foo()); // line 11 - return 0; // line 12 - } - ''' - - dirname = self.get_dir() - src_filename = os.path.join(dirname, 'src.cpp') - out_filename = os.path.join(dirname, 'a.out.js') - no_maps_filename = os.path.join(dirname, 'no-maps.out.js') - - with open(src_filename, 'w') as f: f.write(src) - assert '-g4' not in Building.COMPILER_TEST_OPTS - Building.emcc(src_filename, Settings.serialize() + self.emcc_args + - Building.COMPILER_TEST_OPTS, out_filename) - # the file name may find its way into the generated code, so make sure we - # can do an apples-to-apples comparison by compiling with the same file name - shutil.move(out_filename, no_maps_filename) - with open(no_maps_filename) as f: no_maps_file = f.read() - no_maps_file = re.sub(' *//@.*$', '', no_maps_file, flags=re.MULTILINE) - Building.COMPILER_TEST_OPTS.append('-g4') - - def build_and_check(): - import json - Building.emcc(src_filename, Settings.serialize() + self.emcc_args + - Building.COMPILER_TEST_OPTS, out_filename, stderr=PIPE) - with open(out_filename) as f: out_file = f.read() - # after removing the @line and @sourceMappingURL comments, the build - # result should be identical to the non-source-mapped debug version. - # this is worth checking because the parser AST swaps strings for token - # objects when generating source maps, so we want to make sure the - # optimizer can deal with both types. - out_file = re.sub(' *//@.*$', '', out_file, flags=re.MULTILINE) - def clean(code): - return code.replace('{\n}', '{}') - self.assertIdentical(clean(no_maps_file), clean(out_file)) - map_filename = out_filename + '.map' - data = json.load(open(map_filename, 'r')) - self.assertIdentical(out_filename, data['file']) - self.assertIdentical(src_filename, data['sources'][0]) - self.assertIdentical(src, data['sourcesContent'][0]) - mappings = json.loads(jsrun.run_js( - path_from_root('tools', 'source-maps', 'sourcemap2json.js'), - tools.shared.NODE_JS, [map_filename])) - seen_lines = set() - for m in mappings: - self.assertIdentical(src_filename, m['source']) - seen_lines.add(m['originalLine']) - # ensure that all the 'meaningful' lines in the original code get mapped - assert seen_lines.issuperset([6, 7, 11, 12]) - - # EMCC_DEBUG=2 causes lots of intermediate files to be written, and so - # serves as a stress test for source maps because it needs to correlate - # line numbers across all those files. - old_emcc_debug = os.environ.get('EMCC_DEBUG', None) - os.environ.pop('EMCC_DEBUG', None) - try: - build_and_check() - os.environ['EMCC_DEBUG'] = '2' - build_and_check() - finally: - if old_emcc_debug is not None: - os.environ['EMCC_DEBUG'] = old_emcc_debug - else: - os.environ.pop('EMCC_DEBUG', None) - - def test_exception_source_map(self): - if Settings.USE_TYPED_ARRAYS != 2: return self.skip("doesn't pass without typed arrays") - if '-g4' not in Building.COMPILER_TEST_OPTS: Building.COMPILER_TEST_OPTS.append('-g4') - if NODE_JS not in JS_ENGINES: return self.skip('sourcemapper requires Node to run') - - src = ''' - #include - - __attribute__((noinline)) void foo(int i) { - if (i < 10) throw i; // line 5 - } - - int main() { - int i; - scanf("%d", &i); - foo(i); - return 0; - } - ''' - - def post(filename): - import json - map_filename = filename + '.map' - mappings = json.loads(jsrun.run_js( - path_from_root('tools', 'source-maps', 'sourcemap2json.js'), - tools.shared.NODE_JS, [map_filename])) - with open(filename) as f: lines = f.readlines() - for m in mappings: - if m['originalLine'] == 5 and '__cxa_throw' in lines[m['generatedLine']]: - return - assert False, 'Must label throw statements with line numbers' - - dirname = self.get_dir() - self.build(src, dirname, os.path.join(dirname, 'src.cpp'), post_build=(None, post)) - - def test_linespecific(self): - if Settings.ASM_JS: return self.skip('asm always has corrections on') - - if '-g' not in Building.COMPILER_TEST_OPTS: Building.COMPILER_TEST_OPTS.append('-g') - if self.emcc_args: - self.emcc_args += ['--llvm-opts', '0'] # llvm full opts make the expected failures here not happen - Building.COMPILER_TEST_OPTS += ['--llvm-opts', '0'] - - Settings.CHECK_SIGNS = 0 - Settings.CHECK_OVERFLOWS = 0 - - # Signs - - src = ''' - #include - #include - - int main() - { - int varey = 100; - unsigned int MAXEY = -1; - printf("*%d*\\n", varey >= MAXEY); // 100 >= -1? not in unsigned! - } - ''' - - Settings.CORRECT_SIGNS = 0 - self.do_run(src, '*1*') # This is a fail - we expect 0 - - Settings.CORRECT_SIGNS = 1 - self.do_run(src, '*0*') # Now it will work properly - - # And now let's fix just that one line - Settings.CORRECT_SIGNS = 2 - Settings.CORRECT_SIGNS_LINES = ["src.cpp:9"] - self.do_run(src, '*0*') - - # Fixing the wrong line should not work - Settings.CORRECT_SIGNS = 2 - Settings.CORRECT_SIGNS_LINES = ["src.cpp:3"] - self.do_run(src, '*1*') - - # And reverse the checks with = 2 - Settings.CORRECT_SIGNS = 3 - Settings.CORRECT_SIGNS_LINES = ["src.cpp:3"] - self.do_run(src, '*0*') - Settings.CORRECT_SIGNS = 3 - Settings.CORRECT_SIGNS_LINES = ["src.cpp:9"] - self.do_run(src, '*1*') - - Settings.CORRECT_SIGNS = 0 - - # Overflows - - src = ''' - #include - int main() { - int t = 77; - for (int i = 0; i < 30; i++) { - t = t + t + t + t + t + 1; - } - printf("*%d,%d*\\n", t, t & 127); - return 0; - } - ''' - - correct = '*186854335,63*' - Settings.CORRECT_OVERFLOWS = 0 - try: - self.do_run(src, correct) - raise Exception('UNEXPECTED-PASS') - except Exception, e: - assert 'UNEXPECTED' not in str(e), str(e) - assert 'Expected to find' in str(e), str(e) - - Settings.CORRECT_OVERFLOWS = 1 - self.do_run(src, correct) # Now it will work properly - - # And now let's fix just that one line - Settings.CORRECT_OVERFLOWS = 2 - Settings.CORRECT_OVERFLOWS_LINES = ["src.cpp:6"] - self.do_run(src, correct) - - # Fixing the wrong line should not work - Settings.CORRECT_OVERFLOWS = 2 - Settings.CORRECT_OVERFLOWS_LINES = ["src.cpp:3"] - try: - self.do_run(src, correct) - raise Exception('UNEXPECTED-PASS') - except Exception, e: - assert 'UNEXPECTED' not in str(e), str(e) - assert 'Expected to find' in str(e), str(e) - - # And reverse the checks with = 2 - Settings.CORRECT_OVERFLOWS = 3 - Settings.CORRECT_OVERFLOWS_LINES = ["src.cpp:3"] - self.do_run(src, correct) - Settings.CORRECT_OVERFLOWS = 3 - Settings.CORRECT_OVERFLOWS_LINES = ["src.cpp:6"] - try: - self.do_run(src, correct) - raise Exception('UNEXPECTED-PASS') - except Exception, e: - assert 'UNEXPECTED' not in str(e), str(e) - assert 'Expected to find' in str(e), str(e) - - Settings.CORRECT_OVERFLOWS = 0 - - # Roundings - - src = ''' - #include - #include - - int main() - { - TYPE x = -5; - printf("*%d*", x/2); - x = 5; - printf("*%d*", x/2); - - float y = -5.33; - x = y; - printf("*%d*", x); - y = 5.33; - x = y; - printf("*%d*", x); - - printf("\\n"); - } - ''' - - if Settings.USE_TYPED_ARRAYS != 2: # the errors here are very specific to non-i64 mode 1 - Settings.CORRECT_ROUNDINGS = 0 - self.do_run(src.replace('TYPE', 'long long'), '*-3**2**-6**5*') # JS floor operations, always to the negative. This is an undetected error here! - self.do_run(src.replace('TYPE', 'int'), '*-2**2**-5**5*') # We get these right, since they are 32-bit and we can shortcut using the |0 trick - self.do_run(src.replace('TYPE', 'unsigned int'), '*-2**2**-6**5*') - - Settings.CORRECT_ROUNDINGS = 1 - Settings.CORRECT_SIGNS = 1 # To be correct here, we need sign corrections as well - self.do_run(src.replace('TYPE', 'long long'), '*-2**2**-5**5*') # Correct - self.do_run(src.replace('TYPE', 'int'), '*-2**2**-5**5*') # Correct - self.do_run(src.replace('TYPE', 'unsigned int'), '*2147483645**2**-5**5*') # Correct - Settings.CORRECT_SIGNS = 0 - - if Settings.USE_TYPED_ARRAYS != 2: # the errors here are very specific to non-i64 mode 1 - Settings.CORRECT_ROUNDINGS = 2 - Settings.CORRECT_ROUNDINGS_LINES = ["src.cpp:13"] # Fix just the last mistake - self.do_run(src.replace('TYPE', 'long long'), '*-3**2**-5**5*') - self.do_run(src.replace('TYPE', 'int'), '*-2**2**-5**5*') # Here we are lucky and also get the first one right - self.do_run(src.replace('TYPE', 'unsigned int'), '*-2**2**-5**5*') - - # And reverse the check with = 2 - if Settings.USE_TYPED_ARRAYS != 2: # the errors here are very specific to non-i64 mode 1 - Settings.CORRECT_ROUNDINGS = 3 - Settings.CORRECT_ROUNDINGS_LINES = ["src.cpp:999"] - self.do_run(src.replace('TYPE', 'long long'), '*-2**2**-5**5*') - self.do_run(src.replace('TYPE', 'int'), '*-2**2**-5**5*') - Settings.CORRECT_SIGNS = 1 # To be correct here, we need sign corrections as well - self.do_run(src.replace('TYPE', 'unsigned int'), '*2147483645**2**-5**5*') - Settings.CORRECT_SIGNS = 0 - - def test_exit_status(self): - if self.emcc_args is None: return self.skip('need emcc') - src = r''' - #include - #include - static void cleanup() { - printf("cleanup\n"); - } - - int main() { - atexit(cleanup); // this atexit should still be called - printf("hello, world!\n"); - exit(118); // Unusual exit status to make sure it's working! - } - ''' - open('post.js', 'w').write(''' - Module.addOnExit(function () { - Module.print('I see exit status: ' + EXITSTATUS); - }); - Module.callMain(); - ''') - self.emcc_args += ['-s', 'INVOKE_RUN=0', '--post-js', 'post.js'] - self.do_run(src, 'hello, world!\nexit(118) called\ncleanup\nI see exit status: 118') - - def test_gc(self): - if self.emcc_args == None: return self.skip('needs ta2') - if Settings.ASM_JS: return self.skip('asm cannot support generic function table') - - Settings.GC_SUPPORT = 1 - - src = r''' - #include - #include - #include - - void *global; - - void finalizer(void *ptr, void *arg) { - printf("finalizing %d (global == %d)\n", (int)arg, ptr == global); - } - - void finalizer2(void *ptr, void *arg) { - printf("finalizing2 %d (global == %d)\n", (int)arg, ptr == global); - } - - int main() { - GC_INIT(); - - void *local, *local2, *local3, *local4, *local5, *local6; - - // Hold on to global, drop locals - - global = GC_MALLOC(1024); // rooted since in a static allocation - GC_REGISTER_FINALIZER_NO_ORDER(global, finalizer, 0, 0, 0); - printf("alloc %p\n", global); - - local = GC_MALLOC(1024); // not rooted since stack is not scanned - GC_REGISTER_FINALIZER_NO_ORDER(local, finalizer, (void*)1, 0, 0); - printf("alloc %p\n", local); - - assert((char*)local - (char*)global >= 1024 || (char*)global - (char*)local >= 1024); - - local2 = GC_MALLOC(1024); // no finalizer - printf("alloc %p\n", local2); - - local3 = GC_MALLOC(1024); // with finalizable2 - GC_REGISTER_FINALIZER_NO_ORDER(local3, finalizer2, (void*)2, 0, 0); - printf("alloc %p\n", local); - - local4 = GC_MALLOC(1024); // yet another - GC_REGISTER_FINALIZER_NO_ORDER(local4, finalizer2, (void*)3, 0, 0); - printf("alloc %p\n", local); - - printf("basic test\n"); - - GC_FORCE_COLLECT(); - - printf("*\n"); - - GC_FREE(global); // force free will actually work - - // scanning inside objects - - global = GC_MALLOC(12); - GC_REGISTER_FINALIZER_NO_ORDER(global, finalizer, 0, 0, 0); - local = GC_MALLOC(12); - GC_REGISTER_FINALIZER_NO_ORDER(local, finalizer, (void*)1, 0, 0); - local2 = GC_MALLOC_ATOMIC(12); - GC_REGISTER_FINALIZER_NO_ORDER(local2, finalizer, (void*)2, 0, 0); - local3 = GC_MALLOC(12); - GC_REGISTER_FINALIZER_NO_ORDER(local3, finalizer, (void*)3, 0, 0); - local4 = GC_MALLOC(12); - GC_REGISTER_FINALIZER_NO_ORDER(local4, finalizer, (void*)4, 0, 0); - local5 = GC_MALLOC_UNCOLLECTABLE(12); - // This should never trigger since local5 is uncollectable - GC_REGISTER_FINALIZER_NO_ORDER(local5, finalizer, (void*)5, 0, 0); - - printf("heap size = %d\n", GC_get_heap_size()); - - local4 = GC_REALLOC(local4, 24); - - printf("heap size = %d\n", GC_get_heap_size()); - - local6 = GC_MALLOC(12); - GC_REGISTER_FINALIZER_NO_ORDER(local6, finalizer, (void*)6, 0, 0); - // This should be the same as a free - GC_REALLOC(local6, 0); - - void **globalData = (void**)global; - globalData[0] = local; - globalData[1] = local2; - - void **localData = (void**)local; - localData[0] = local3; - - void **local2Data = (void**)local2; - local2Data[0] = local4; // actually ignored, because local2 is atomic, so 4 is freeable - - printf("object scan test test\n"); - - GC_FORCE_COLLECT(); - - printf("*\n"); - - GC_FREE(global); // force free will actually work - - printf("*\n"); - - GC_FORCE_COLLECT(); - - printf(".\n"); - - global = 0; - - return 0; - } - ''' - self.do_run(src, '''basic test -finalizing 1 (global == 0) -finalizing2 2 (global == 0) -finalizing2 3 (global == 0) -* -finalizing 0 (global == 1) -heap size = 72 -heap size = 84 -finalizing 6 (global == 0) -object scan test test -finalizing 4 (global == 0) -* -finalizing 0 (global == 1) -* -finalizing 1 (global == 0) -finalizing 2 (global == 0) -finalizing 3 (global == 0) -. -''') - - # Generate tests for everything - def make_run(fullname, name=-1, compiler=-1, embetter=0, quantum_size=0, - typed_arrays=0, emcc_args=None, env=None): - - if env is None: env = {} - - TT = type(fullname, (T,), dict(run_name = fullname, env = env)) - - def tearDown(self): - super(TT, self).tearDown() - - for k, v in self.env.iteritems(): - del os.environ[k] - - # clear global changes to Building - Building.COMPILER_TEST_OPTS = [] - Building.COMPILER = CLANG - Building.LLVM_OPTS = 0 - - TT.tearDown = tearDown - - def setUp(self): - super(TT, self).setUp() - for k, v in self.env.iteritems(): - assert k not in os.environ, k + ' should not be in environment' - os.environ[k] = v - - global checked_sanity - if not checked_sanity: - print '(checking sanity from test runner)' # do this after we set env stuff - check_sanity(force=True) - checked_sanity = True - - Building.COMPILER_TEST_OPTS = ['-g'] - os.chdir(self.get_dir()) # Ensure the directory exists and go there - Building.COMPILER = compiler - - self.emcc_args = None if emcc_args is None else emcc_args[:] - if self.emcc_args is not None: - Settings.load(self.emcc_args) - Building.LLVM_OPTS = 0 - if '-O2' in self.emcc_args: - Building.COMPILER_TEST_OPTS = [] # remove -g in -O2 tests, for more coverage - #Building.COMPILER_TEST_OPTS += self.emcc_args - for arg in self.emcc_args: - if arg.startswith('-O'): - Building.COMPILER_TEST_OPTS.append(arg) # so bitcode is optimized too, this is for cpp to ll - else: - try: - key, value = arg.split('=') - Settings[key] = value # forward -s K=V - except: - pass - return - - # TODO: Move much of these to a init() function in shared.py, and reuse that - Settings.USE_TYPED_ARRAYS = typed_arrays - Settings.INVOKE_RUN = 1 - Settings.RELOOP = 0 # we only do them in the "o2" pass - Settings.MICRO_OPTS = embetter - Settings.QUANTUM_SIZE = quantum_size - Settings.ASSERTIONS = 1-embetter - Settings.SAFE_HEAP = 1-embetter - Settings.CHECK_OVERFLOWS = 1-embetter - Settings.CORRECT_OVERFLOWS = 1-embetter - Settings.CORRECT_SIGNS = 0 - Settings.CORRECT_ROUNDINGS = 0 - Settings.CORRECT_OVERFLOWS_LINES = CORRECT_SIGNS_LINES = CORRECT_ROUNDINGS_LINES = SAFE_HEAP_LINES = [] - Settings.CHECK_SIGNS = 0 #1-embetter - Settings.RUNTIME_TYPE_INFO = 0 - Settings.DISABLE_EXCEPTION_CATCHING = 0 - Settings.INCLUDE_FULL_LIBRARY = 0 - Settings.BUILD_AS_SHARED_LIB = 0 - Settings.RUNTIME_LINKED_LIBS = [] - Settings.EMULATE_UNALIGNED_ACCESSES = int(Settings.USE_TYPED_ARRAYS == 2 and Building.LLVM_OPTS == 2) - Settings.DOUBLE_MODE = 1 if Settings.USE_TYPED_ARRAYS and Building.LLVM_OPTS == 0 else 0 - Settings.PRECISE_I64_MATH = 0 - Settings.NAMED_GLOBALS = 0 if not embetter else 1 - - TT.setUp = setUp - - return TT - - # Make one run with the defaults - default = make_run("default", compiler=CLANG, emcc_args=[]) - - # Make one run with -O1, with safe heap - o1 = make_run("o1", compiler=CLANG, emcc_args=["-O1", "-s", "ASM_JS=0", "-s", "SAFE_HEAP=1"]) - - # Make one run with -O2, but without closure (we enable closure in specific tests, otherwise on everything it is too slow) - o2 = make_run("o2", compiler=CLANG, emcc_args=["-O2", "-s", "ASM_JS=0", "-s", "JS_CHUNK_SIZE=1024"]) - - # asm.js - asm1 = make_run("asm1", compiler=CLANG, emcc_args=["-O1", "-s", "CHECK_HEAP_ALIGN=1"]) - asm2 = make_run("asm2", compiler=CLANG, emcc_args=["-O2"]) - asm2g = make_run("asm2g", compiler=CLANG, emcc_args=["-O2", "-g", "-s", "ASSERTIONS=1", "--memory-init-file", "1"]) - asm2x86 = make_run("asm2x86", compiler=CLANG, emcc_args=["-O2", "-g", "-s", "CHECK_HEAP_ALIGN=1"], env={"EMCC_LLVM_TARGET": "i386-pc-linux-gnu"}) - - # Make custom runs with various options - for compiler, quantum, embetter, typed_arrays in [ - (CLANG, 4, 0, 0), - (CLANG, 4, 1, 1), - ]: - fullname = 's_0_%d%s%s' % ( - embetter, '' if quantum == 4 else '_q' + str(quantum), '' if typed_arrays in [0, 1] else '_t' + str(typed_arrays) - ) - locals()[fullname] = make_run(fullname, fullname, compiler, embetter, quantum, typed_arrays) - - del T # T is just a shape for the specific subclasses, we don't test it itself - - class other(RunnerCore): - def test_emcc(self): - for compiler in [EMCC, EMXX]: - shortcompiler = os.path.basename(compiler) - suffix = '.c' if compiler == EMCC else '.cpp' - - # --version - output = Popen([PYTHON, compiler, '--version'], stdout=PIPE, stderr=PIPE).communicate() - output = output[0].replace('\r', '') - self.assertContained('''emcc (Emscripten GCC-like replacement)''', output) - self.assertContained('''Copyright (C) 2013 the Emscripten authors (see AUTHORS.txt) -This is free and open source software under the MIT license. -There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -''', output) - - # -v, without input files - output = Popen([PYTHON, compiler, '-v'], stdout=PIPE, stderr=PIPE).communicate() - self.assertContained('''clang version''', output[1].replace('\r', ''), output[1].replace('\r', '')) - - # --help - output = Popen([PYTHON, compiler, '--help'], stdout=PIPE, stderr=PIPE).communicate() - self.assertContained('''%s [options] file... - -Most normal gcc/g++ options will work, for example: - --help Display this information - --version Display compiler version information - -Options that are modified or new in %s include: - -O0 No optimizations (default) -''' % (shortcompiler, shortcompiler), output[0].replace('\r', ''), output[1].replace('\r', '')) - - # emcc src.cpp ==> writes a.out.js - self.clear() - output = Popen([PYTHON, compiler, path_from_root('tests', 'hello_world' + suffix)], stdout=PIPE, stderr=PIPE).communicate() - assert len(output[0]) == 0, output[0] - assert os.path.exists('a.out.js'), '\n'.join(output) - self.assertContained('hello, world!', run_js('a.out.js')) - - # properly report source code errors, and stop there - self.clear() - assert not os.path.exists('a.out.js') - process = Popen([PYTHON, compiler, path_from_root('tests', 'hello_world_error' + suffix)], stdout=PIPE, stderr=PIPE) - output = process.communicate() - assert not os.path.exists('a.out.js'), 'compilation failed, so no output file is expected' - assert len(output[0]) == 0, output[0] - assert process.returncode is not 0, 'Failed compilation must return a nonzero error code!' - self.assertNotContained('IOError', output[1]) # no python stack - self.assertNotContained('Traceback', output[1]) # no python stack - self.assertContained('error: invalid preprocessing directive', output[1]) - self.assertContained(["error: use of undeclared identifier 'cheez", "error: unknown type name 'cheez'"], output[1]) - self.assertContained('errors generated', output[1]) - assert 'compiler frontend failed to generate LLVM bitcode, halting' in output[1].split('errors generated.')[1] - - # emcc src.cpp -c and emcc src.cpp -o src.[o|bc] ==> should give a .bc file - # regression check: -o js should create "js", with bitcode content - for args in [['-c'], ['-o', 'src.o'], ['-o', 'src.bc'], ['-o', 'src.so'], ['-o', 'js']]: - target = args[1] if len(args) == 2 else 'hello_world.o' - self.clear() - Popen([PYTHON, compiler, path_from_root('tests', 'hello_world' + suffix)] + args, stdout=PIPE, stderr=PIPE).communicate() - syms = Building.llvm_nm(target) - assert len(syms.defs) == 1 and 'main' in syms.defs, 'Failed to generate valid bitcode' - if target == 'js': # make sure emcc can recognize the target as a bitcode file - shutil.move(target, target + '.bc') - target += '.bc' - output = Popen([PYTHON, compiler, target, '-o', target + '.js'], stdout = PIPE, stderr = PIPE).communicate() - assert len(output[0]) == 0, output[0] - assert os.path.exists(target + '.js'), 'Expected %s to exist since args are %s : %s' % (target + '.js', str(args), '\n'.join(output)) - self.assertContained('hello, world!', run_js(target + '.js')) - - # handle singleton archives - self.clear() - Popen([PYTHON, compiler, path_from_root('tests', 'hello_world' + suffix), '-o', 'a.bc'], stdout=PIPE, stderr=PIPE).communicate() - Popen([LLVM_AR, 'r', 'a.a', 'a.bc'], stdout=PIPE, stderr=PIPE).communicate() - assert os.path.exists('a.a') - output = Popen([PYTHON, compiler, 'a.a']).communicate() - assert os.path.exists('a.out.js'), output - self.assertContained('hello, world!', run_js('a.out.js')) - - # emcc src.ll ==> generates .js - self.clear() - output = Popen([PYTHON, compiler, path_from_root('tests', 'hello_world.ll')], stdout=PIPE, stderr=PIPE).communicate() - assert len(output[0]) == 0, output[0] - assert os.path.exists('a.out.js'), '\n'.join(output) - self.assertContained('hello, world!', run_js('a.out.js')) - - # emcc [..] -o [path] ==> should work with absolute paths - try: - for path in [os.path.abspath(os.path.join('..', 'file1.js')), os.path.join('b_dir', 'file2.js')]: - print path - self.clear(in_curr=True) - os.chdir(self.get_dir()) - if not os.path.exists('a_dir'): os.mkdir('a_dir') - os.chdir('a_dir') - if not os.path.exists('b_dir'): os.mkdir('b_dir') - output = Popen([PYTHON, compiler, path_from_root('tests', 'hello_world.ll'), '-o', path], stdout=PIPE, stderr=PIPE).communicate() - print output - assert os.path.exists(path), path + ' does not exist; ' + '\n'.join(output) - last = os.getcwd() - os.chdir(os.path.dirname(path)) - self.assertContained('hello, world!', run_js(os.path.basename(path))) - os.chdir(last) - finally: - os.chdir(self.get_dir()) - self.clear() - - # dlmalloc. dlmalloc is special in that it is the only part of libc that is (1) hard to write well, and - # very speed-sensitive. So we do not implement it in JS in library.js, instead we compile it from source - for source, has_malloc in [('hello_world' + suffix, False), ('hello_malloc.cpp', True)]: - print source, has_malloc - self.clear() - output = Popen([PYTHON, compiler, path_from_root('tests', source)], stdout=PIPE, stderr=PIPE).communicate() - assert os.path.exists('a.out.js'), '\n'.join(output) - self.assertContained('hello, world!', run_js('a.out.js')) - generated = open('a.out.js').read() - assert ('function _malloc(bytes) {' in generated) == (not has_malloc), 'If malloc is needed, it should be there, if not not' - - # Optimization: emcc src.cpp -o something.js [-Ox]. -O0 is the same as not specifying any optimization setting - for params, opt_level, bc_params, closure, has_malloc in [ # bc params are used after compiling to bitcode - (['-o', 'something.js'], 0, None, 0, 1), - (['-o', 'something.js', '-O0'], 0, None, 0, 0), - (['-o', 'something.js', '-O1'], 1, None, 0, 0), - (['-o', 'something.js', '-O1', '-g'], 1, None, 0, 0), # no closure since debug - (['-o', 'something.js', '-O1', '--closure', '1'], 1, None, 1, 0), - (['-o', 'something.js', '-O1', '--closure', '1', '-s', 'ASM_JS=0'], 1, None, 1, 0), - (['-o', 'something.js', '-O2'], 2, None, 0, 1), - (['-o', 'something.js', '-O2', '-g'], 2, None, 0, 0), - (['-o', 'something.js', '-Os'], 2, None, 0, 1), - (['-o', 'something.js', '-O3', '-s', 'ASM_JS=0'], 3, None, 1, 1), - # and, test compiling to bitcode first - (['-o', 'something.bc'], 0, [], 0, 0), - (['-o', 'something.bc', '-O0'], 0, [], 0, 0), - (['-o', 'something.bc', '-O1'], 1, ['-O1'], 0, 0), - (['-o', 'something.bc', '-O2'], 2, ['-O2'], 0, 0), - (['-o', 'something.bc', '-O3'], 3, ['-O3', '-s', 'ASM_JS=0'], 1, 0), - (['-O1', '-o', 'something.bc'], 1, [], 0, 0), - ]: - print params, opt_level, bc_params, closure, has_malloc - self.clear() - keep_debug = '-g' in params - args = [PYTHON, compiler, path_from_root('tests', 'hello_world_loop' + ('_malloc' if has_malloc else '') + '.cpp')] + params - print '..', args - output = Popen(args, - stdout=PIPE, stderr=PIPE).communicate() - assert len(output[0]) == 0, output[0] - if bc_params is not None: - assert os.path.exists('something.bc'), output[1] - bc_args = [PYTHON, compiler, 'something.bc', '-o', 'something.js'] + bc_params - print '....', bc_args - output = Popen(bc_args, stdout=PIPE, stderr=PIPE).communicate() - assert os.path.exists('something.js'), output[1] - assert ('Applying some potentially unsafe optimizations!' in output[1]) == (opt_level >= 3), 'unsafe warning should appear in opt >= 3' - self.assertContained('hello, world!', run_js('something.js')) - - # Verify optimization level etc. in the generated code - # XXX these are quite sensitive, and will need updating when code generation changes - generated = open('something.js').read() # TODO: parse out the _main function itself, not support code, if the tests below need that some day - assert 'new Uint16Array' in generated and 'new Uint32Array' in generated, 'typed arrays 2 should be used by default' - assert 'SAFE_HEAP' not in generated, 'safe heap should not be used by default' - assert ': while(' not in generated, 'when relooping we also js-optimize, so there should be no labelled whiles' - if closure: - if opt_level == 0: assert '._main =' in generated, 'closure compiler should have been run' - elif opt_level >= 1: assert '._main=' in generated, 'closure compiler should have been run (and output should be minified)' - else: - # closure has not been run, we can do some additional checks. TODO: figure out how to do these even with closure - assert '._main = ' not in generated, 'closure compiler should not have been run' - if keep_debug: - assert ('(label)' in generated or '(label | 0)' in generated) == (opt_level <= 1), 'relooping should be in opt >= 2' - assert ('assert(STACKTOP < STACK_MAX' in generated) == (opt_level == 0), 'assertions should be in opt == 0' - assert 'var $i;' in generated or 'var $i_0' in generated or 'var $storemerge3;' in generated or 'var $storemerge4;' in generated or 'var $i_04;' in generated or 'var $original = 0' in generated, 'micro opts should always be on' - if opt_level >= 2 and '-g' in params: - assert re.search('HEAP8\[\$?\w+ ?\+ ?\(+\$?\w+ ?', generated) or re.search('HEAP8\[HEAP32\[', generated), 'eliminator should create compound expressions, and fewer one-time vars' # also in -O1, but easier to test in -O2 - assert ('_puts(' in generated) == (opt_level >= 1), 'with opt >= 1, llvm opts are run and they should optimize printf to puts' - if opt_level == 0 or '-g' in params: assert 'function _main() {' in generated, 'Should be unminified, including whitespace' - elif opt_level >= 2: assert ('function _main(){' in generated or '"use asm";var a=' in generated), 'Should be whitespace-minified' - - # emcc -s RELOOP=1 src.cpp ==> should pass -s to emscripten.py. --typed-arrays is a convenient alias for -s USE_TYPED_ARRAYS - for params, test, text in [ - (['-O2'], lambda generated: 'function intArrayToString' in generated, 'shell has unminified utilities'), - (['-O2', '--closure', '1'], lambda generated: 'function intArrayToString' not in generated, 'closure minifies the shell'), - (['-O2'], lambda generated: 'var b=0' in generated and not 'function _main' in generated, 'registerize/minify is run by default in -O2'), - (['-O2', '--minify', '0'], lambda generated: 'var b = 0' in generated and not 'function _main' in generated, 'minify is cancelled, but not registerize'), - (['-O2', '--js-opts', '0'], lambda generated: 'var b=0' not in generated and 'var b = 0' not in generated and 'function _main' in generated, 'js opts are cancelled'), - (['-O2', '-g'], lambda generated: 'var b=0' not in generated and 'var b = 0' not in generated and 'function _main' in generated, 'registerize/minify is cancelled by -g'), - (['-O2', '-g0'], lambda generated: 'var b=0' in generated and not 'function _main' in generated, 'registerize/minify is run by default in -O2 -g0'), - (['-O2', '-g1'], lambda generated: 'var b = 0' in generated and not 'function _main' in generated, 'compress is cancelled by -g1'), - (['-O2', '-g2'], lambda generated: ('var b = 0' in generated or 'var i1 = 0' in generated) and 'function _main' in generated, 'minify is cancelled by -g2'), - (['-O2', '-g3'], lambda generated: 'var b=0' not in generated and 'var b = 0' not in generated and 'function _main' in generated, 'registerize is cancelled by -g3'), - #(['-O2', '-g4'], lambda generated: 'var b=0' not in generated and 'var b = 0' not in generated and 'function _main' in generated, 'same as -g3 for now'), - (['-s', 'INLINING_LIMIT=0'], lambda generated: 'function _dump' in generated, 'no inlining without opts'), - (['-O3', '-s', 'INLINING_LIMIT=0', '--closure', '0'], lambda generated: 'function _dump' not in generated, 'lto/inlining'), - (['-Os', '--llvm-lto', '1', '-s', 'ASM_JS=0'], lambda generated: 'function _dump' in generated, '-Os disables inlining'), - (['-s', 'USE_TYPED_ARRAYS=0'], lambda generated: 'new Int32Array' not in generated, 'disable typed arrays'), - (['-s', 'USE_TYPED_ARRAYS=1'], lambda generated: 'IHEAPU = ' in generated, 'typed arrays 1 selected'), - ([], lambda generated: 'Module["_dump"]' not in generated, 'dump is not exported by default'), - (['-s', 'EXPORTED_FUNCTIONS=["_main", "_dump"]'], lambda generated: 'Module["_dump"]' in generated, 'dump is now exported'), - (['--typed-arrays', '0'], lambda generated: 'new Int32Array' not in generated, 'disable typed arrays'), - (['--typed-arrays', '1'], lambda generated: 'IHEAPU = ' in generated, 'typed arrays 1 selected'), - (['--typed-arrays', '2'], lambda generated: 'new Uint16Array' in generated and 'new Uint32Array' in generated, 'typed arrays 2 selected'), - (['--llvm-opts', '1'], lambda generated: '_puts(' in generated, 'llvm opts requested'), - ]: - print params, text - self.clear() - output = Popen([PYTHON, compiler, path_from_root('tests', 'hello_world_loop.cpp'), '-o', 'a.out.js'] + params, stdout=PIPE, stderr=PIPE).communicate() - assert len(output[0]) == 0, output[0] - assert os.path.exists('a.out.js'), '\n'.join(output) - self.assertContained('hello, world!', run_js('a.out.js')) - assert test(open('a.out.js').read()), text - - # Compiling two source files into a final JS. - for args, target in [([], 'a.out.js'), (['-o', 'combined.js'], 'combined.js')]: - self.clear() - output = Popen([PYTHON, compiler, path_from_root('tests', 'twopart_main.cpp'), path_from_root('tests', 'twopart_side.cpp')] + args, - stdout=PIPE, stderr=PIPE).communicate() - assert len(output[0]) == 0, output[0] - assert os.path.exists(target), '\n'.join(output) - self.assertContained('side got: hello from main, over', run_js(target)) - - # Compiling two files with -c will generate separate .bc files - self.clear() - output = Popen([PYTHON, compiler, path_from_root('tests', 'twopart_main.cpp'), path_from_root('tests', 'twopart_side.cpp'), '-c'] + args, - stdout=PIPE, stderr=PIPE).communicate() - if '-o' in args: - # specifying -o and -c is an error - assert 'fatal error' in output[1], output[1] - continue - - assert os.path.exists('twopart_main.o'), '\n'.join(output) - assert os.path.exists('twopart_side.o'), '\n'.join(output) - assert not os.path.exists(target), 'We should only have created bitcode here: ' + '\n'.join(output) - - # Compiling one of them alone is expected to fail - output = Popen([PYTHON, compiler, 'twopart_main.o', '-O1', '-g'] + args, stdout=PIPE, stderr=PIPE).communicate() - assert os.path.exists(target), '\n'.join(output) - #print '\n'.join(output) - self.assertContained('missing function', run_js(target, stderr=STDOUT)) - try_delete(target) - - # Combining those bc files into js should work - output = Popen([PYTHON, compiler, 'twopart_main.o', 'twopart_side.o'] + args, stdout=PIPE, stderr=PIPE).communicate() - assert os.path.exists(target), '\n'.join(output) - self.assertContained('side got: hello from main, over', run_js(target)) - - # Combining bc files into another bc should also work - try_delete(target) - assert not os.path.exists(target) - output = Popen([PYTHON, compiler, 'twopart_main.o', 'twopart_side.o', '-o', 'combined.bc'] + args, stdout=PIPE, stderr=PIPE).communicate() - syms = Building.llvm_nm('combined.bc') - assert len(syms.defs) == 2 and 'main' in syms.defs, 'Failed to generate valid bitcode' - output = Popen([PYTHON, compiler, 'combined.bc', '-o', 'combined.bc.js'], stdout = PIPE, stderr = PIPE).communicate() - assert len(output[0]) == 0, output[0] - assert os.path.exists('combined.bc.js'), 'Expected %s to exist' % ('combined.bc.js') - self.assertContained('side got: hello from main, over', run_js('combined.bc.js')) - - # --js-transform - self.clear() - trans = os.path.join(self.get_dir(), 't.py') - trans_file = open(trans, 'w') - trans_file.write(''' -import sys -f = open(sys.argv[1], 'w') -f.write('transformed!') -f.close() -''') - trans_file.close() - output = Popen([PYTHON, compiler, path_from_root('tests', 'hello_world' + suffix), '--js-transform', '%s t.py' % (PYTHON)], stdout=PIPE, stderr=PIPE).communicate() - assert open('a.out.js').read() == 'transformed!', 'Transformed output must be as expected' - - # TODO: Add in files test a clear example of using disablePermissions, and link to it from the wiki - # TODO: test normal project linking, static and dynamic: get_library should not need to be told what to link! - # TODO: deprecate llvm optimizations, dlmalloc, etc. in emscripten.py. - - def test_cmake(self): - # On Windows, we want to build cmake-generated Makefiles with mingw32-make instead of e.g. cygwin make, since mingw32-make - # understands Windows paths, and cygwin make additionally produces a cryptic 'not valid bitcode file' errors on files that - # *are* valid bitcode files. - - if os.name == 'nt': - make_command = 'mingw32-make' - emscriptencmaketoolchain = path_from_root('cmake', 'Platform', 'Emscripten.cmake') - else: - make_command = 'make' - emscriptencmaketoolchain = path_from_root('cmake', 'Platform', 'Emscripten_unix.cmake') - - cmake_cases = ['target_js', 'target_html'] - cmake_outputs = ['hello_world.js', 'hello_world_gles.html'] - for i in range(0, 2): - for configuration in ['Debug', 'Release']: - - # Create a temp workspace folder - cmakelistsdir = path_from_root('tests', 'cmake', cmake_cases[i]) - tempdirname = tempfile.mkdtemp(prefix='emscripten_test_' + self.__class__.__name__ + '_', dir=TEMP_DIR) - try: - os.chdir(tempdirname) - - # Run Cmake - cmd = ['cmake', '-DCMAKE_TOOLCHAIN_FILE='+emscriptencmaketoolchain, - '-DCMAKE_BUILD_TYPE=' + configuration, - '-DCMAKE_MODULE_PATH=' + path_from_root('cmake').replace('\\', '/'), - '-G' 'Unix Makefiles', cmakelistsdir] - ret = Popen(cmd, stdout=PIPE, stderr=PIPE).communicate() - if ret[1] != None and len(ret[1].strip()) > 0: - print >> sys.stderr, ret[1] # If there were any errors, print them directly to console for diagnostics. - if 'error' in ret[1].lower(): - print >> sys.stderr, 'Failed command: ' + ' '.join(cmd) - print >> sys.stderr, 'Result:\n' + ret[1] - raise Exception('cmake call failed!') - assert os.path.exists(tempdirname + '/Makefile'), 'CMake call did not produce a Makefile!' - - # Build - cmd = [make_command] - ret = Popen(cmd, stdout=PIPE).communicate() - if ret[1] != None and len(ret[1].strip()) > 0: - print >> sys.stderr, ret[1] # If there were any errors, print them directly to console for diagnostics. - if 'error' in ret[0].lower() and not '0 error(s)' in ret[0].lower(): - print >> sys.stderr, 'Failed command: ' + ' '.join(cmd) - print >> sys.stderr, 'Result:\n' + ret[0] - raise Exception('make failed!') - assert os.path.exists(tempdirname + '/' + cmake_outputs[i]), 'Building a cmake-generated Makefile failed to produce an output file %s!' % tempdirname + '/' + cmake_outputs[i] - - # Run through node, if CMake produced a .js file. - if cmake_outputs[i].endswith('.js'): - ret = Popen(listify(NODE_JS) + [tempdirname + '/' + cmake_outputs[i]], stdout=PIPE).communicate()[0] - assert 'hello, world!' in ret, 'Running cmake-based .js application failed!' - finally: - os.chdir(path_from_root('tests')) # Move away from the directory we are about to remove. - shutil.rmtree(tempdirname) - - def test_nostdincxx(self): - try: - old = os.environ.get('EMCC_LLVM_TARGET') or '' - for compiler in [EMCC, EMXX]: - for target in ['i386-pc-linux-gnu', 'le32-unknown-nacl']: - print compiler, target - os.environ['EMCC_LLVM_TARGET'] = target - out, err = Popen([PYTHON, EMCC, path_from_root('tests', 'hello_world.cpp'), '-v'], stdout=PIPE, stderr=PIPE).communicate() - out2, err2 = Popen([PYTHON, EMCC, path_from_root('tests', 'hello_world.cpp'), '-v', '-nostdinc++'], stdout=PIPE, stderr=PIPE).communicate() - assert out == out2 - def focus(e): - assert 'search starts here:' in e, e - assert e.count('End of search list.') == 1, e - return e[e.index('search starts here:'):e.index('End of search list.')+20] - err = focus(err) - err2 = focus(err2) - assert err == err2, err + '\n\n\n\n' + err2 - finally: - if old: - os.environ['EMCC_LLVM_TARGET'] = old - - def test_failure_error_code(self): - for compiler in [EMCC, EMXX]: - # Test that if one file is missing from the build, then emcc shouldn't succeed, and shouldn't try to produce an output file. - process = Popen([PYTHON, compiler, path_from_root('tests', 'hello_world.c'), 'this_file_is_missing.c', '-o', 'this_output_file_should_never_exist.js'], stdout=PIPE, stderr=PIPE) - process.communicate() - assert process.returncode is not 0, 'Trying to compile a nonexisting file should return with a nonzero error code!' - assert os.path.exists('this_output_file_should_never_exist.js') == False, 'Emcc should not produce an output file when build fails!' - - def test_cxx03(self): - for compiler in [EMCC, EMXX]: - process = Popen([PYTHON, compiler, path_from_root('tests', 'hello_cxx03.cpp')], stdout=PIPE, stderr=PIPE) - process.communicate() - assert process.returncode is 0, 'By default, emscripten should build using -std=c++03!' - - def test_cxx11(self): - for compiler in [EMCC, EMXX]: - process = Popen([PYTHON, compiler, '-std=c++11', path_from_root('tests', 'hello_cxx11.cpp')], stdout=PIPE, stderr=PIPE) - process.communicate() - assert process.returncode is 0, 'User should be able to specify custom -std= on the command line!' - - def test_catch_undef(self): - open(os.path.join(self.get_dir(), 'test.cpp'), 'w').write(r''' - #include - #include - - class Test { - public: - std::vector vector; - }; - - Test globalInstance; - - int main() { - printf("hello, world!\n"); - return 0; - } - ''') - Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'test.cpp'), '-fsanitize=undefined']).communicate() - self.assertContained('hello, world!', run_js(os.path.join(self.get_dir(), 'a.out.js'))) - - def test_unaligned_memory(self): - open(os.path.join(self.get_dir(), 'test.cpp'), 'w').write(r''' - #include - - typedef unsigned char Bit8u; - typedef unsigned short Bit16u; - typedef unsigned int Bit32u; - - int main() - { - Bit8u data[4] = {0x01,0x23,0x45,0x67}; - - printf("data: %x\n", *(Bit32u*)data); - printf("data[0,1] 16bit: %x\n", *(Bit16u*)data); - printf("data[1,2] 16bit: %x\n", *(Bit16u*)(data+1)); - } - ''') - Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'test.cpp'), '-s', 'UNALIGNED_MEMORY=1']).communicate() - self.assertContained('data: 67452301\ndata[0,1] 16bit: 2301\ndata[1,2] 16bit: 4523', run_js(os.path.join(self.get_dir(), 'a.out.js'))) - - def test_unaligned_memory_2(self): - open(os.path.join(self.get_dir(), 'test.cpp'), 'w').write(r''' - #include - #include - - int main( int argc, char ** argv ) - { - std::string testString( "Hello, World!" ); - - printf( "testString = %s\n", testString.c_str() ); - return 0; - } - ''') - Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'test.cpp'), '-s', 'UNALIGNED_MEMORY=1']).communicate() - self.assertContained('testString = Hello, World!', run_js(os.path.join(self.get_dir(), 'a.out.js'))) - - def test_asm_minify(self): - def test(args): - Popen([PYTHON, EMCC, path_from_root('tests', 'hello_world_loop_malloc.cpp')] + args).communicate() - self.assertContained('hello, world!', run_js(self.in_dir('a.out.js'))) - return open(self.in_dir('a.out.js')).read() - - src = test([]) - assert 'function _malloc' in src - - src = test(['-O2', '-s', 'ASM_JS=1']) - normal_size = len(src) - print 'normal', normal_size - assert 'function _malloc' not in src - - src = test(['-O2', '-s', 'ASM_JS=1', '--minify', '0']) - unminified_size = len(src) - print 'unminified', unminified_size - assert unminified_size > normal_size - assert 'function _malloc' not in src - - src = test(['-O2', '-s', 'ASM_JS=1', '-g']) - debug_size = len(src) - print 'debug', debug_size - assert debug_size > unminified_size - assert 'function _malloc' in src - - def test_dangerous_func_cast(self): - src = r''' - #include - typedef void (*voidfunc)(); - int my_func() { - printf("my func\n"); - return 10; - } - int main(int argc, char **argv) { - voidfunc fps[10]; - for (int i = 0; i < 10; i++) fps[i] = (i == argc) ? (void (*)())my_func : NULL; - fps[2*(argc-1) + 1](); - return 0; - } - ''' - open('src.c', 'w').write(src) - def test(args, expected, err_expected=None): - out, err = Popen([PYTHON, EMCC, 'src.c'] + args, stderr=PIPE).communicate() - if err_expected: self.assertContained(err_expected, err) - self.assertContained(expected, run_js(self.in_dir('a.out.js'), stderr=PIPE, full_output=True)) - return open(self.in_dir('a.out.js')).read() - - test([], 'my func') # no asm, so casting func works - test(['-O2'], 'abort', ['Casting potentially incompatible function pointer i32 ()* to void (...)*, for my_func', - 'Incompatible function pointer casts are very dangerous with ASM_JS=1, you should investigate and correct these']) # asm, so failure - test(['-O2', '-s', 'ASSERTIONS=1'], - 'Invalid function pointer called. Perhaps a miscast function pointer (check compilation warnings) or bad vtable lookup (maybe due to derefing a bad pointer, like NULL)?', - ['Casting potentially incompatible function pointer i32 ()* to void (...)*, for my_func', - 'Incompatible function pointer casts are very dangerous with ASM_JS=1, you should investigate and correct these']) # asm, so failure - - def test_l_link(self): - # Linking with -lLIBNAME and -L/DIRNAME should work - - open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write(''' - extern void printey(); - int main() { - printey(); - return 0; - } - ''') - - try: - os.makedirs(os.path.join(self.get_dir(), 'libdir')); - except: - pass - - open(os.path.join(self.get_dir(), 'libdir', 'libfile.cpp'), 'w').write(''' - #include - void printey() { - printf("hello from lib\\n"); - } - ''') - - Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'libdir', 'libfile.cpp'), '-c']).communicate() - shutil.move(os.path.join(self.get_dir(), 'libfile.o'), os.path.join(self.get_dir(), 'libdir', 'libfile.so')) - Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '-L' + os.path.join(self.get_dir(), 'libdir'), '-lfile']).communicate() - self.assertContained('hello from lib', run_js(os.path.join(self.get_dir(), 'a.out.js'))) - assert not os.path.exists('a.out') and not os.path.exists('a.exe'), 'Must not leave unneeded linker stubs' - - def test_static_link(self): - def test(name, header, main, side, expected, args=[], suffix='cpp', first=True): - print name - #t = main ; main = side ; side = t - original_main = main - original_side = side - if header: open(os.path.join(self.get_dir(), 'header.h'), 'w').write(header) - if type(main) == str: - open(os.path.join(self.get_dir(), 'main.' + suffix), 'w').write(main) - main = ['main.' + suffix] - if type(side) == str: - open(os.path.join(self.get_dir(), 'side.' + suffix), 'w').write(side) - side = ['side.' + suffix] - Popen([PYTHON, EMCC] + side + ['-o', 'side.js', '-s', 'SIDE_MODULE=1', '-O2'] + args).communicate() - # TODO: test with and without DISABLE_GL_EMULATION, check that file sizes change - Popen([PYTHON, EMCC] + main + ['-o', 'main.js', '-s', 'MAIN_MODULE=1', '-O2', '-s', 'DISABLE_GL_EMULATION=1'] + args).communicate() - Popen([PYTHON, EMLINK, 'main.js', 'side.js', 'together.js'], stdout=PIPE).communicate() - assert os.path.exists('together.js') - for engine in JS_ENGINES: - out = run_js('together.js', engine=engine, stderr=PIPE, full_output=True) - self.assertContained(expected, out) - if engine == SPIDERMONKEY_ENGINE: self.validate_asmjs(out) - if first: - shutil.copyfile('together.js', 'first.js') - test(name + ' (reverse)', header, original_side, original_main, expected, args, suffix, False) # test reverse order - - # test a simple call from one module to another. only one has a string (and constant memory initialization for it) - test('basics', '', ''' - #include - extern int sidey(); - int main() { - printf("other says %d.", sidey()); - return 0; - } - ''', ''' - int sidey() { return 11; } - ''', 'other says 11.') - - # finalization of float variables should pass asm.js validation - test('floats', '', ''' - #include - extern float sidey(); - int main() { - printf("other says %.2f.", sidey()+1); - return 0; - } - ''', ''' - float sidey() { return 11.5; } - ''', 'other says 12.50') - - # memory initialization in both - test('multiple memory inits', '', r''' - #include - extern void sidey(); - int main() { - printf("hello from main\n"); - sidey(); - return 0; - } - ''', r''' - #include - void sidey() { printf("hello from side\n"); } - ''', 'hello from main\nhello from side\n') - - # function pointers - test('fp1', 'typedef void (*voidfunc)();', r''' - #include - #include "header.h" - voidfunc sidey(voidfunc f); - void a() { printf("hello from funcptr\n"); } - int main() { - sidey(a)(); - return 0; - } - ''', ''' - #include "header.h" - voidfunc sidey(voidfunc f) { return f; } - ''', 'hello from funcptr\n') - - # function pointers with 'return' in the name - test('fp2', 'typedef void (*voidfunc)();', r''' - #include - #include "header.h" - int sidey(voidfunc f); - void areturn0() { printf("hello 0\n"); } - void areturn1() { printf("hello 1\n"); } - void areturn2() { printf("hello 2\n"); } - int main(int argc, char **argv) { - voidfunc table[3] = { areturn0, areturn1, areturn2 }; - table[sidey(NULL)](); - return 0; - } - ''', ''' - #include "header.h" - int sidey(voidfunc f) { if (f) f(); return 1; } - ''', 'hello 1\n') - - # Global initializer - test('global init', '', r''' - #include - struct Class { - Class() { printf("a new Class\n"); } - }; - static Class c; - int main() { - return 0; - } - ''', r''' - void nothing() {} - ''', 'a new Class\n') - - # Multiple global initializers (LLVM generates overlapping names for them) - test('global inits', r''' - #include - struct Class { - Class(const char *name) { printf("new %s\n", name); } - }; - ''', r''' - #include "header.h" - static Class c("main"); - int main() { - return 0; - } - ''', r''' - #include "header.h" - static Class c("side"); - ''', ['new main\nnew side\n', 'new side\nnew main\n']) - - # Class code used across modules - test('codecall', r''' - #include - struct Class { - Class(const char *name); - }; - ''', r''' - #include "header.h" - int main() { - Class c("main"); - return 0; - } - ''', r''' - #include "header.h" - Class::Class(const char *name) { printf("new %s\n", name); } - ''', ['new main\n']) - - # malloc usage in both modules - test('malloc', r''' - #include - #include - char *side(const char *data); - ''', r''' - #include - #include "header.h" - int main() { - char *temp = side("hello through side\n"); - char *ret = (char*)malloc(strlen(temp)+1); - strcpy(ret, temp); - temp[1] = 'x'; - puts(ret); - return 0; - } - ''', r''' - #include "header.h" - char *side(const char *data) { - char *ret = (char*)malloc(strlen(data)+1); - strcpy(ret, data); - return ret; - } - ''', ['hello through side\n']) - - # libc usage in one modules. must force libc inclusion in the main module if that isn't the one using mallinfo() - try: - os.environ['EMCC_FORCE_STDLIBS'] = 'libc' - test('malloc-1', r''' - #include - int side(); - ''', r''' - #include - #include "header.h" - int main() { - printf("|%d|\n", side()); - return 0; - } - ''', r''' - #include - #include - #include "header.h" - int side() { - struct mallinfo m = mallinfo(); - return m.arena > 1; - } - ''', ['|1|\n']) - finally: - del os.environ['EMCC_FORCE_STDLIBS'] - - # iostream usage in one and std::string in both - test('iostream', r''' - #include - #include - std::string side(); - ''', r''' - #include "header.h" - int main() { - std::cout << "hello from main " << side() << std::endl; - return 0; - } - ''', r''' - #include "header.h" - std::string side() { return "and hello from side"; } - ''', ['hello from main and hello from side\n']) - - # followup to iostream test: a second linking - print 'second linking of a linking output' - open('moar.cpp', 'w').write(r''' - #include - struct Moar { - Moar() { std::cout << "moar!" << std::endl; } - }; - Moar m; - ''') - Popen([PYTHON, EMCC, 'moar.cpp', '-o', 'moar.js', '-s', 'SIDE_MODULE=1', '-O2']).communicate() - Popen([PYTHON, EMLINK, 'together.js', 'moar.js', 'triple.js'], stdout=PIPE).communicate() - assert os.path.exists('triple.js') - for engine in JS_ENGINES: - out = run_js('triple.js', engine=engine, stderr=PIPE, full_output=True) - self.assertContained('moar!\nhello from main and hello from side\n', out) - if engine == SPIDERMONKEY_ENGINE: self.validate_asmjs(out) - - # zlib compression library. tests function pointers in initializers and many other things - test('zlib', '', open(path_from_root('tests', 'zlib', 'example.c'), 'r').read(), - self.get_library('zlib', os.path.join('libz.a'), make_args=['libz.a']), - open(path_from_root('tests', 'zlib', 'ref.txt'), 'r').read(), - args=['-I' + path_from_root('tests', 'zlib')], suffix='c') - - # bullet physics engine. tests all the things - test('bullet', '', open(path_from_root('tests', 'bullet', 'Demos', 'HelloWorld', 'HelloWorld.cpp'), 'r').read(), - self.get_library('bullet', [os.path.join('src', '.libs', 'libBulletDynamics.a'), - os.path.join('src', '.libs', 'libBulletCollision.a'), - os.path.join('src', '.libs', 'libLinearMath.a')]), - [open(path_from_root('tests', 'bullet', 'output.txt'), 'r').read(), # different roundings - open(path_from_root('tests', 'bullet', 'output2.txt'), 'r').read(), - open(path_from_root('tests', 'bullet', 'output3.txt'), 'r').read()], - args=['-I' + path_from_root('tests', 'bullet', 'src')]) - - - def test_outline(self): - def test(name, src, libs, expected, expected_ranges, args=[], suffix='cpp'): - print name - - def measure_funcs(filename): - i = 0 - start = -1 - curr = None - ret = {} - for line in open(filename): - i += 1 - if line.startswith('function '): - start = i - curr = line - elif line.startswith('}') and curr: - size = i - start - ret[curr] = size - curr = None - return ret - - for debug, outlining_limits in [ - ([], (1000,)), - (['-g1'], (1000,)), - (['-g2'], (1000,)), - (['-g'], (100, 250, 500, 1000, 2000, 5000, 0)) - ]: - for outlining_limit in outlining_limits: - print '\n', Building.COMPILER_TEST_OPTS, debug, outlining_limit, '\n' - # TODO: test without -g3, tell all sorts - Popen([PYTHON, EMCC, src] + libs + ['-o', 'test.js', '-O2'] + debug + ['-s', 'OUTLINING_LIMIT=%d' % outlining_limit] + args).communicate() - assert os.path.exists('test.js') - shutil.copyfile('test.js', '%d_test.js' % outlining_limit) - for engine in JS_ENGINES: - out = run_js('test.js', engine=engine, stderr=PIPE, full_output=True) - self.assertContained(expected, out) - if engine == SPIDERMONKEY_ENGINE: self.validate_asmjs(out) - if debug == ['-g']: - low = expected_ranges[outlining_limit][0] - seen = max(measure_funcs('test.js').values()) - high = expected_ranges[outlining_limit][1] - print Building.COMPILER_TEST_OPTS, outlining_limit, ' ', low, '<=', seen, '<=', high - assert low <= seen <= high - - for test_opts, expected_ranges in [ - ([], { - 100: (190, 250), - 250: (200, 330), - 500: (250, 500), - 1000: (230, 1000), - 2000: (380, 2000), - 5000: (800, 5000), - 0: (1500, 5000) - }), - (['-O2'], { - 100: (0, 1500), - 250: (0, 1500), - 500: (0, 1500), - 1000: (0, 1500), - 2000: (0, 2000), - 5000: (0, 5000), - 0: (0, 5000) - }), - ]: - Building.COMPILER_TEST_OPTS = test_opts - test('zlib', path_from_root('tests', 'zlib', 'example.c'), - self.get_library('zlib', os.path.join('libz.a'), make_args=['libz.a']), - open(path_from_root('tests', 'zlib', 'ref.txt'), 'r').read(), - expected_ranges, - args=['-I' + path_from_root('tests', 'zlib')], suffix='c') - - def test_symlink(self): - if os.name == 'nt': - return self.skip('Windows FS does not need to be tested for symlinks support, since it does not have them.') - open(os.path.join(self.get_dir(), 'foobar.xxx'), 'w').write('int main(){ return 0; }') - os.symlink(os.path.join(self.get_dir(), 'foobar.xxx'), os.path.join(self.get_dir(), 'foobar.c')) - Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'foobar.c'), '-o', os.path.join(self.get_dir(), 'foobar')], stdout=PIPE, stderr=PIPE).communicate() - assert os.path.exists(os.path.join(self.get_dir(), 'foobar')) - try_delete(os.path.join(self.get_dir(), 'foobar')) - try_delete(os.path.join(self.get_dir(), 'foobar.xxx')) - try_delete(os.path.join(self.get_dir(), 'foobar.c')) - - open(os.path.join(self.get_dir(), 'foobar.c'), 'w').write('int main(){ return 0; }') - os.symlink(os.path.join(self.get_dir(), 'foobar.c'), os.path.join(self.get_dir(), 'foobar.xxx')) - Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'foobar.xxx'), '-o', os.path.join(self.get_dir(), 'foobar')], stdout=PIPE, stderr=PIPE).communicate() - assert os.path.exists(os.path.join(self.get_dir(), 'foobar')) - try_delete(os.path.join(self.get_dir(), 'foobar')) - try_delete(os.path.join(self.get_dir(), 'foobar.xxx')) - try_delete(os.path.join(self.get_dir(), 'foobar.c')) - - def test_multiply_defined_libsymbols(self): - lib = "int mult() { return 1; }" - lib_name = os.path.join(self.get_dir(), 'libA.c') - open(lib_name, 'w').write(lib) - a2 = "void x() {}" - a2_name = os.path.join(self.get_dir(), 'a2.c') - open(a2_name, 'w').write(a2) - b2 = "void y() {}" - b2_name = os.path.join(self.get_dir(), 'b2.c') - open(b2_name, 'w').write(b2) - main = r''' - #include - int mult(); - int main() { - printf("result: %d\n", mult()); - return 0; - } - ''' - main_name = os.path.join(self.get_dir(), 'main.c') - open(main_name, 'w').write(main) - - Building.emcc(lib_name, output_filename='libA.so') - - Building.emcc(a2_name, ['-L.', '-lA']) - Building.emcc(b2_name, ['-L.', '-lA']) - - Building.emcc(main_name, ['-L.', '-lA', a2_name+'.o', b2_name+'.o'], output_filename='a.out.js') - - self.assertContained('result: 1', run_js(os.path.join(self.get_dir(), 'a.out.js'))) - - def test_multiply_defined_libsymbols_2(self): - a = "int x() { return 55; }" - a_name = os.path.join(self.get_dir(), 'a.c') - open(a_name, 'w').write(a) - b = "int y() { return 2; }" - b_name = os.path.join(self.get_dir(), 'b.c') - open(b_name, 'w').write(b) - c = "int z() { return 5; }" - c_name = os.path.join(self.get_dir(), 'c.c') - open(c_name, 'w').write(c) - main = r''' - #include - int x(); - int y(); - int z(); - int main() { - printf("result: %d\n", x() + y() + z()); - return 0; - } - ''' - main_name = os.path.join(self.get_dir(), 'main.c') - open(main_name, 'w').write(main) - - Building.emcc(a_name) # a.c.o - Building.emcc(b_name) # b.c.o - Building.emcc(c_name) # c.c.o - lib_name = os.path.join(self.get_dir(), 'libLIB.a') - Building.emar('cr', lib_name, [a_name + '.o', b_name + '.o']) # libLIB.a with a and b - - # a is in the lib AND in an .o, so should be ignored in the lib. We do still need b from the lib though - Building.emcc(main_name, ['-L.', '-lLIB', a_name+'.o', c_name + '.o'], output_filename='a.out.js') - - self.assertContained('result: 62', run_js(os.path.join(self.get_dir(), 'a.out.js'))) - - def test_redundant_link(self): - lib = "int mult() { return 1; }" - lib_name = os.path.join(self.get_dir(), 'libA.c') - open(lib_name, 'w').write(lib) - main = r''' - #include - int mult(); - int main() { - printf("result: %d\n", mult()); - return 0; - } - ''' - main_name = os.path.join(self.get_dir(), 'main.c') - open(main_name, 'w').write(main) - - Building.emcc(lib_name, output_filename='libA.so') - - Building.emcc(main_name, ['libA.so']*2, output_filename='a.out.js') - - self.assertContained('result: 1', run_js(os.path.join(self.get_dir(), 'a.out.js'))) - - def test_export_all(self): - lib = r''' - #include - void libf1() { printf("libf1\n"); } - void libf2() { printf("libf2\n"); } - ''' - lib_name = os.path.join(self.get_dir(), 'lib.c') - open(lib_name, 'w').write(lib) - - open('main.js', 'w').write(''' - _libf1(); - _libf2(); - ''') - - Building.emcc(lib_name, ['-s', 'EXPORT_ALL=1', '--post-js', 'main.js'], output_filename='a.out.js') - - self.assertContained('libf1\nlibf2\n', run_js(os.path.join(self.get_dir(), 'a.out.js'))) - - def test_stdin(self): - open('main.cpp', 'w').write(r''' -#include -int main(int argc, char const *argv[]) -{ - char str[10] = {0}; - scanf("%10s", str); - printf("%s\n", str); - return 0; -} -''') - Building.emcc('main.cpp', output_filename='a.out.js') - open('in.txt', 'w').write('abc') - # node's stdin support is broken - self.assertContained('abc', Popen(listify(SPIDERMONKEY_ENGINE) + ['a.out.js'], stdin=open('in.txt'), stdout=PIPE, stderr=PIPE).communicate()[0]) - - def test_ungetc_fscanf(self): - open('main.cpp', 'w').write(r''' - #include - int main(int argc, char const *argv[]) - { - char str[4] = {0}; - FILE* f = fopen("my_test.input", "r"); - if (f == NULL) { - printf("cannot open file\n"); - return -1; - } - ungetc('x', f); - ungetc('y', f); - ungetc('z', f); - fscanf(f, "%3s", str); - printf("%s\n", str); - return 0; - } - ''') - open('my_test.input', 'w').write('abc') - Building.emcc('main.cpp', ['--embed-file', 'my_test.input'], output_filename='a.out.js') - self.assertContained('zyx', Popen(listify(JS_ENGINES[0]) + ['a.out.js'], stdout=PIPE, stderr=PIPE).communicate()[0]) - - def test_abspaths(self): - # Includes with absolute paths are generally dangerous, things like -I/usr/.. will get to system local headers, not our portable ones. - - shutil.copyfile(path_from_root('tests', 'hello_world.c'), 'main.c') - - for args, expected in [(['-I/usr/something'], True), - (['-L/usr/something'], True), - (['-I/usr/something', '-Wno-warn-absolute-paths'], False), - (['-L/usr/something', '-Wno-warn-absolute-paths'], False), - (['-Isubdir/something'], False), - (['-Lsubdir/something'], False), - ([], False)]: - err = Popen([PYTHON, EMCC, 'main.c'] + args, stderr=PIPE).communicate()[1] - assert ('encountered. If this is to a local system header/library, it may cause problems (local system files make sense for compiling natively on your system, but not necessarily to JavaScript)' in err) == expected, err - - def test_local_link(self): - # Linking a local library directly, like /usr/lib/libsomething.so, cannot work of course since it - # doesn't contain bitcode. However, when we see that we should look for a bitcode file for that - # library in the -L paths and system/lib - open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write(''' - extern void printey(); - int main() { - printey(); - return 0; - } - ''') - - try: - os.makedirs(os.path.join(self.get_dir(), 'subdir')); - except: - pass - open(os.path.join(self.get_dir(), 'subdir', 'libfile.so'), 'w').write('this is not llvm bitcode!') - - open(os.path.join(self.get_dir(), 'libfile.cpp'), 'w').write(''' - #include - void printey() { - printf("hello from lib\\n"); - } - ''') - - Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'libfile.cpp'), '-o', 'libfile.so']).communicate() - Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), os.path.join(self.get_dir(), 'subdir', 'libfile.so'), '-L.'], stderr=PIPE).communicate() - self.assertContained('hello from lib', run_js(os.path.join(self.get_dir(), 'a.out.js'))) - - def test_runtimelink_multi(self): - return self.skip('shared libs are deprecated') - if Settings.ASM_JS: return self.skip('asm does not support runtime linking yet') - - if SPIDERMONKEY_ENGINE not in JS_ENGINES: return self.skip('cannot run without spidermonkey due to node limitations') - - open('testa.h', 'w').write(r''' - #ifndef _TESTA_H_ - #define _TESTA_H_ - - class TestA { - public: - TestA(); - }; - - #endif - ''') - open('testb.h', 'w').write(r''' - #ifndef _TESTB_H_ - #define _TESTB_H_ - - class TestB { - public: - TestB(); - }; - - #endif - ''') - open('testa.cpp', 'w').write(r''' - #include - #include - - TestA::TestA() { - printf("TestA\n"); - } - ''') - open('testb.cpp', 'w').write(r''' - #include - #include - #include - /* - */ - TestB::TestB() { - printf("TestB\n"); - TestA* testa = new TestA(); - } - ''') - open('main.cpp', 'w').write(r''' - #include - #include - #include - - /* - */ - int main(int argc, char** argv) { - printf("Main\n"); - TestA* testa = new TestA(); - TestB* testb = new TestB(); - } - ''') - - Popen([PYTHON, EMCC, 'testa.cpp', '-o', 'liba.js', '-s', 'BUILD_AS_SHARED_LIB=2', '-s', 'LINKABLE=1', '-s', 'NAMED_GLOBALS=1', '-I.']).communicate() - Popen([PYTHON, EMCC, 'testb.cpp', '-o', 'libb.js', '-s', 'BUILD_AS_SHARED_LIB=2', '-s', 'LINKABLE=1', '-s', 'NAMED_GLOBALS=1', '-I.']).communicate() - Popen([PYTHON, EMCC, 'main.cpp', '-o', 'main.js', '-s', 'RUNTIME_LINKED_LIBS=["liba.js", "libb.js"]', '-s', 'NAMED_GLOBALS=1', '-I.', '-s', 'LINKABLE=1']).communicate() - - Popen([PYTHON, EMCC, 'main.cpp', 'testa.cpp', 'testb.cpp', '-o', 'full.js', '-I.']).communicate() - - self.assertContained('TestA\nTestB\nTestA\n', run_js('main.js', engine=SPIDERMONKEY_ENGINE)) - - def test_js_libraries(self): - open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write(''' - #include - extern "C" { - extern void printey(); - extern int calcey(int x, int y); - } - int main() { - printey(); - printf("*%d*\\n", calcey(10, 22)); - return 0; - } - ''') - open(os.path.join(self.get_dir(), 'mylib1.js'), 'w').write(''' - mergeInto(LibraryManager.library, { - printey: function() { - Module.print('hello from lib!'); - } - }); - ''') - open(os.path.join(self.get_dir(), 'mylib2.js'), 'w').write(''' - mergeInto(LibraryManager.library, { - calcey: function(x, y) { - return x + y; - } - }); - ''') - - Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--js-library', os.path.join(self.get_dir(), 'mylib1.js'), - '--js-library', os.path.join(self.get_dir(), 'mylib2.js')]).communicate() - self.assertContained('hello from lib!\n*32*\n', run_js(os.path.join(self.get_dir(), 'a.out.js'))) - - def test_identical_basenames(self): - # Issue 287: files in different dirs but with the same basename get confused as the same, - # causing multiply defined symbol errors - try: - os.makedirs(os.path.join(self.get_dir(), 'foo')); - except: - pass - try: - os.makedirs(os.path.join(self.get_dir(), 'bar')); - except: - pass - open(os.path.join(self.get_dir(), 'foo', 'main.cpp'), 'w').write(''' - extern void printey(); - int main() { - printey(); - return 0; - } - ''') - open(os.path.join(self.get_dir(), 'bar', 'main.cpp'), 'w').write(''' - #include - void printey() { printf("hello there\\n"); } - ''') - - Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'foo', 'main.cpp'), os.path.join(self.get_dir(), 'bar', 'main.cpp')]).communicate() - self.assertContained('hello there', run_js(os.path.join(self.get_dir(), 'a.out.js'))) - - # ditto with first creating .o files - try_delete(os.path.join(self.get_dir(), 'a.out.js')) - Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'foo', 'main.cpp'), '-o', os.path.join(self.get_dir(), 'foo', 'main.o')]).communicate() - Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'bar', 'main.cpp'), '-o', os.path.join(self.get_dir(), 'bar', 'main.o')]).communicate() - Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'foo', 'main.o'), os.path.join(self.get_dir(), 'bar', 'main.o')]).communicate() - self.assertContained('hello there', run_js(os.path.join(self.get_dir(), 'a.out.js'))) - - def test_main_a(self): - # if main() is in a .a, we need to pull in that .a - - main_name = os.path.join(self.get_dir(), 'main.c') - open(main_name, 'w').write(r''' - #include - extern int f(); - int main() { - printf("result: %d.\n", f()); - return 0; - } - ''') - - other_name = os.path.join(self.get_dir(), 'other.c') - open(other_name, 'w').write(r''' - #include - int f() { return 12346; } - ''') - - Popen([PYTHON, EMCC, main_name, '-c', '-o', main_name+'.bc']).communicate() - Popen([PYTHON, EMCC, other_name, '-c', '-o', other_name+'.bc']).communicate() - - Popen([PYTHON, EMAR, 'cr', main_name+'.a', main_name+'.bc']).communicate() - - Popen([PYTHON, EMCC, other_name+'.bc', main_name+'.a']).communicate() - - self.assertContained('result: 12346.', run_js(os.path.join(self.get_dir(), 'a.out.js'))) - - def test_dup_o_in_a(self): - open('common.c', 'w').write(r''' - #include - void a(void) { - printf("a\n"); - } - ''') - Popen([PYTHON, EMCC, 'common.c', '-c', '-o', 'common.o']).communicate() - Popen([PYTHON, EMAR, 'rc', 'liba.a', 'common.o']).communicate() - - open('common.c', 'w').write(r''' - #include - void b(void) { - printf("b\n"); - } - ''') - Popen([PYTHON, EMCC, 'common.c', '-c', '-o', 'common.o']).communicate() - Popen([PYTHON, EMAR, 'rc', 'libb.a', 'common.o']).communicate() - - open('main.c', 'w').write(r''' - void a(void); - void b(void); - int main() { - a(); - b(); - } - ''') - Popen([PYTHON, EMCC, 'main.c', '-L.', '-la', '-lb']).communicate() - - self.assertContained('a\nb\n', run_js(os.path.join(self.get_dir(), 'a.out.js'))) - - def test_export_in_a(self): - export_name = 'this_is_an_entry_point' - - open('export.c', 'w').write(r''' - #include - void %s(void) { - printf("Hello, world!\n"); - } - ''' % export_name) - Popen([PYTHON, EMCC, 'export.c', '-c', '-o', 'export.o']).communicate() - Popen([PYTHON, EMAR, 'rc', 'libexport.a', 'export.o']).communicate() - - open('main.c', 'w').write(r''' - int main() { - return 0; - } - ''') - - definition = 'function _%s(' % export_name - - # Sanity check: the symbol should not be linked in if not requested. - Popen([PYTHON, EMCC, 'main.c', '-L.', '-lexport']).communicate() - self.assertNotContained(definition, open(os.path.join(self.get_dir(), 'a.out.js')).read()) - - # Sanity check: exporting without a definition does not cause it to appear. - # Note: exporting main prevents emcc from warning that it generated no code. - Popen([PYTHON, EMCC, 'main.c', '-s', '''EXPORTED_FUNCTIONS=['_main', '_%s']''' % export_name]).communicate() - self.assertNotContained(definition, open(os.path.join(self.get_dir(), 'a.out.js')).read()) - - # Actual test: defining symbol in library and exporting it causes it to appear in the output. - Popen([PYTHON, EMCC, 'main.c', '-L.', '-lexport', '-s', '''EXPORTED_FUNCTIONS=['_%s']''' % export_name]).communicate() - self.assertContained(definition, open(os.path.join(self.get_dir(), 'a.out.js')).read()) - - def test_embed_file(self): - open(os.path.join(self.get_dir(), 'somefile.txt'), 'w').write('''hello from a file with lots of data and stuff in it thank you very much''') - open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write(r''' - #include - int main() { - FILE *f = fopen("somefile.txt", "r"); - char buf[100]; - fread(buf, 1, 20, f); - buf[20] = 0; - fclose(f); - printf("|%s|\n", buf); - return 0; - } - ''') - - Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--embed-file', 'somefile.txt']).communicate() - self.assertContained('|hello from a file wi|', run_js(os.path.join(self.get_dir(), 'a.out.js'))) - - # preload twice, should not err - Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--embed-file', 'somefile.txt', '--embed-file', 'somefile.txt']).communicate() - self.assertContained('|hello from a file wi|', run_js(os.path.join(self.get_dir(), 'a.out.js'))) - - def test_embed_file_dup(self): - try_delete(os.path.join(self.get_dir(), 'tst')) - os.mkdir(os.path.join(self.get_dir(), 'tst')) - os.mkdir(os.path.join(self.get_dir(), 'tst', 'test1')) - os.mkdir(os.path.join(self.get_dir(), 'tst', 'test2')) - - open(os.path.join(self.get_dir(), 'tst', 'aa.txt'), 'w').write('''frist''') - open(os.path.join(self.get_dir(), 'tst', 'test1', 'aa.txt'), 'w').write('''sacond''') - open(os.path.join(self.get_dir(), 'tst', 'test2', 'aa.txt'), 'w').write('''thard''') - open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write(r''' - #include - #include - void print_file(const char *name) { - FILE *f = fopen(name, "r"); - char buf[100]; - memset(buf, 0, 100); - fread(buf, 1, 20, f); - buf[20] = 0; - fclose(f); - printf("|%s|\n", buf); - } - int main() { - print_file("tst/aa.txt"); - print_file("tst/test1/aa.txt"); - print_file("tst/test2/aa.txt"); - return 0; - } - ''') - - Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--embed-file', 'tst']).communicate() - self.assertContained('|frist|\n|sacond|\n|thard|\n', run_js(os.path.join(self.get_dir(), 'a.out.js'))) - - def test_multidynamic_link(self): - # Linking the same dynamic library in will error, normally, since we statically link it, causing dupe symbols - # A workaround is to use --ignore-dynamic-linking, see emcc --help for details - - open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write(r''' - #include - extern void printey(); - extern void printother(); - int main() { - printf("*"); - printey(); - printf("\n"); - printother(); - printf("\n"); - printf("*"); - return 0; - } - ''') - - try: - os.makedirs(os.path.join(self.get_dir(), 'libdir')); - except: - pass - - open(os.path.join(self.get_dir(), 'libdir', 'libfile.cpp'), 'w').write(''' - #include - void printey() { - printf("hello from lib"); - } - ''') - - open(os.path.join(self.get_dir(), 'libdir', 'libother.cpp'), 'w').write(''' - #include - extern void printey(); - void printother() { - printf("|"); - printey(); - printf("|"); - } - ''') - - # This lets us link the same dynamic lib twice. We will need to link it in manually at the end. - compiler = [PYTHON, EMCC, '--ignore-dynamic-linking'] - - # Build libfile normally into an .so - Popen(compiler + [os.path.join(self.get_dir(), 'libdir', 'libfile.cpp'), '-o', os.path.join(self.get_dir(), 'libdir', 'libfile.so')]).communicate() - # Build libother and dynamically link it to libfile - but add --ignore-dynamic-linking - Popen(compiler + [os.path.join(self.get_dir(), 'libdir', 'libother.cpp'), '-L' + os.path.join(self.get_dir(), 'libdir'), '-lfile', '-o', os.path.join(self.get_dir(), 'libdir', 'libother.so')]).communicate() - # Build the main file, linking in both the libs - Popen(compiler + [os.path.join(self.get_dir(), 'main.cpp'), '-L' + os.path.join(self.get_dir(), 'libdir'), '-lfile', '-lother', '-c']).communicate() - - # The normal build system is over. We need to do an additional step to link in the dynamic libraries, since we ignored them before - Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.o'), '-L' + os.path.join(self.get_dir(), 'libdir'), '-lfile', '-lother']).communicate() - - self.assertContained('*hello from lib\n|hello from lib|\n*', run_js(os.path.join(self.get_dir(), 'a.out.js'))) - - def test_js_link(self): - open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write(''' - #include - int main() { - printf("hello from main\\n"); - return 0; - } - ''') - open(os.path.join(self.get_dir(), 'before.js'), 'w').write(''' - var MESSAGE = 'hello from js'; - if (typeof Module != 'undefined') throw 'This code should run before anything else!'; - ''') - open(os.path.join(self.get_dir(), 'after.js'), 'w').write(''' - Module.print(MESSAGE); - ''') - - Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--pre-js', 'before.js', '--post-js', 'after.js']).communicate() - self.assertContained('hello from main\nhello from js\n', run_js(os.path.join(self.get_dir(), 'a.out.js'))) - - def test_sdl_endianness(self): - open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write(r''' - #include - #include - - int main() { - printf("%d, %d, %d\n", SDL_BYTEORDER, SDL_LIL_ENDIAN, SDL_BIG_ENDIAN); - return 0; - } - ''') - Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp')]).communicate() - self.assertContained('1234, 1234, 4321\n', run_js(os.path.join(self.get_dir(), 'a.out.js'))) - - def test_link_memcpy(self): - # memcpy can show up *after* optimizations, so after our opportunity to link in libc, so it must be special-cased - open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write(r''' - #include - - int main(int argc, char **argv) { - int num = argc + 10; - char buf[num], buf2[num]; - for (int i = 0; i < num; i++) { - buf[i] = i*i+i/3; - } - for (int i = 1; i < num; i++) { - buf[i] += buf[i-1]; - } - for (int i = 0; i < num; i++) { - buf2[i] = buf[i]; - } - for (int i = 1; i < num; i++) { - buf2[i] += buf2[i-1]; - } - for (int i = 0; i < num; i++) { - printf("%d:%d\n", i, buf2[i]); - } - return 0; - } - ''') - Popen([PYTHON, EMCC, '-O2', os.path.join(self.get_dir(), 'main.cpp')]).communicate() - output = run_js(os.path.join(self.get_dir(), 'a.out.js'), full_output=True, stderr=PIPE) - self.assertContained('''0:0 -1:1 -2:6 -3:21 -4:53 -5:111 -6:-49 -7:98 -8:55 -9:96 -10:-16 -''', output) - self.assertNotContained('warning: library.js memcpy should not be running, it is only for testing!', output) - - def test_warn_undefined(self): - open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write(r''' - #include - - extern "C" { - void something(); - } - - int main() { - something(); - return 0; - } - ''') - - def clear(): try_delete('a.out.js') - - for args in [[], ['-O2']]: - clear() - print 'warn', args - output = Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '-s', 'WARN_ON_UNDEFINED_SYMBOLS=1'] + args, stderr=PIPE).communicate() - self.assertContained('unresolved symbol: something', output[1]) - - clear() - output = Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp')] + args, stderr=PIPE).communicate() - self.assertNotContained('unresolved symbol: something\n', output[1]) - - for args in [[], ['-O2']]: - clear() - print 'error', args - output = Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '-s', 'ERROR_ON_UNDEFINED_SYMBOLS=1'] + args, stderr=PIPE).communicate() - self.assertContained('unresolved symbol: something', output[1]) - assert not os.path.exists('a.out.js') - - clear() - output = Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp')] + args, stderr=PIPE).communicate() - self.assertNotContained('unresolved symbol: something\n', output[1]) - assert os.path.exists('a.out.js') - - def test_toobig(self): - open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write(r''' - #include - - #define BYTES 100*1024*1024 - - int main(int argc, char **argv) { - if (argc == 100) { - static char buf[BYTES]; - static char buf2[BYTES]; - for (int i = 0; i < BYTES; i++) { - buf[i] = i*i; - buf2[i] = i/3; - } - for (int i = 0; i < BYTES; i++) { - buf[i] = buf2[i/2]; - buf2[i] = buf[i/3]; - } - printf("%d\n", buf[10] + buf2[20]); - } - return 0; - } - ''') - output = Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp')], stderr=PIPE).communicate()[1] - assert 'Emscripten failed' in output, output - assert 'warning: very large fixed-size structural type' in output, output - - def test_prepost(self): - open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write(''' - #include - int main() { - printf("hello from main\\n"); - return 0; - } - ''') - open(os.path.join(self.get_dir(), 'pre.js'), 'w').write(''' - var Module = { - preRun: function() { Module.print('pre-run') }, - postRun: function() { Module.print('post-run') } - }; - ''') - - Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--pre-js', 'pre.js']).communicate() - self.assertContained('pre-run\nhello from main\npost-run\n', run_js(os.path.join(self.get_dir(), 'a.out.js'))) - - # never run, so no preRun or postRun - src = open(os.path.join(self.get_dir(), 'a.out.js')).read().replace('// {{PRE_RUN_ADDITIONS}}', 'addRunDependency()') - open(os.path.join(self.get_dir(), 'a.out.js'), 'w').write(src) - self.assertNotContained('pre-run\nhello from main\npost-run\n', run_js(os.path.join(self.get_dir(), 'a.out.js'))) - - # noInitialRun prevents run - for no_initial_run, run_dep in [(0, 0), (1, 0), (0, 1)]: - print no_initial_run, run_dep - Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp')]).communicate() - src = 'var Module = { noInitialRun: %d };\n' % no_initial_run + open(os.path.join(self.get_dir(), 'a.out.js')).read() - if run_dep: - src = src.replace('// {{PRE_RUN_ADDITIONS}}', '// {{PRE_RUN_ADDITIONS}}\naddRunDependency("test");') \ - .replace('// {{POST_RUN_ADDITIONS}}', '// {{POST_RUN_ADDITIONS}}\nremoveRunDependency("test");') - open(os.path.join(self.get_dir(), 'a.out.js'), 'w').write(src) - assert ('hello from main' in run_js(os.path.join(self.get_dir(), 'a.out.js'))) != no_initial_run, 'only run if no noInitialRun' - - if no_initial_run: - # Calling main later should still work, filesystem etc. must be set up. - print 'call main later' - src = open(os.path.join(self.get_dir(), 'a.out.js')).read() + '\nModule.callMain();\n'; - open(os.path.join(self.get_dir(), 'a.out.js'), 'w').write(src) - assert 'hello from main' in run_js(os.path.join(self.get_dir(), 'a.out.js')), 'main should print when called manually' - - # Use postInit - open(os.path.join(self.get_dir(), 'pre.js'), 'w').write(''' - var Module = { - preRun: function() { Module.print('pre-run') }, - postRun: function() { Module.print('post-run') }, - preInit: function() { Module.print('pre-init') } - }; - ''') - Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--pre-js', 'pre.js']).communicate() - self.assertContained('pre-init\npre-run\nhello from main\npost-run\n', run_js(os.path.join(self.get_dir(), 'a.out.js'))) - - def test_prepost2(self): - open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write(''' - #include - int main() { - printf("hello from main\\n"); - return 0; - } - ''') - open(os.path.join(self.get_dir(), 'pre.js'), 'w').write(''' - var Module = { - preRun: function() { Module.print('pre-run') }, - }; - ''') - open(os.path.join(self.get_dir(), 'pre2.js'), 'w').write(''' - Module.postRun = function() { Module.print('post-run') }; - ''') - Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--pre-js', 'pre.js', '--pre-js', 'pre2.js']).communicate() - self.assertContained('pre-run\nhello from main\npost-run\n', run_js(os.path.join(self.get_dir(), 'a.out.js'))) - - def test_prepre(self): - open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write(''' - #include - int main() { - printf("hello from main\\n"); - return 0; - } - ''') - open(os.path.join(self.get_dir(), 'pre.js'), 'w').write(''' - var Module = { - preRun: [function() { Module.print('pre-run') }], - }; - ''') - open(os.path.join(self.get_dir(), 'pre2.js'), 'w').write(''' - Module.preRun.push(function() { Module.print('prepre') }); - ''') - Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--pre-js', 'pre.js', '--pre-js', 'pre2.js']).communicate() - self.assertContained('prepre\npre-run\nhello from main\n', run_js(os.path.join(self.get_dir(), 'a.out.js'))) - - def test_save_bc(self): - for save in [0, 1]: - self.clear() - Popen([PYTHON, EMCC, path_from_root('tests', 'hello_world_loop_malloc.cpp')] + ([] if not save else ['--save-bc', self.in_dir('my_bitcode.bc')])).communicate() - assert 'hello, world!' in run_js(self.in_dir('a.out.js')) - assert os.path.exists(self.in_dir('my_bitcode.bc')) == save - if save: - try_delete('a.out.js') - Building.llvm_dis(self.in_dir('my_bitcode.bc'), self.in_dir('my_ll.ll')) - try: - os.environ['EMCC_LEAVE_INPUTS_RAW'] = '1' - Popen([PYTHON, EMCC, 'my_ll.ll', '-o', 'two.js']).communicate() - assert 'hello, world!' in run_js(self.in_dir('two.js')) - finally: - del os.environ['EMCC_LEAVE_INPUTS_RAW'] - - 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 [ - (path_from_root('tools', 'test-js-optimizer.js'), open(path_from_root('tools', 'test-js-optimizer-output.js')).read(), - ['hoistMultiples', 'loopOptimizer', 'removeAssignsToUndefined', 'simplifyExpressions']), - (path_from_root('tools', 'test-js-optimizer-t2c.js'), open(path_from_root('tools', 'test-js-optimizer-t2c-output.js')).read(), - ['simplifyExpressions', 'optimizeShiftsConservative']), - (path_from_root('tools', 'test-js-optimizer-t2.js'), open(path_from_root('tools', 'test-js-optimizer-t2-output.js')).read(), - ['simplifyExpressions', 'optimizeShiftsAggressive']), - # Make sure that optimizeShifts handles functions with shift statements. - (path_from_root('tools', 'test-js-optimizer-t3.js'), open(path_from_root('tools', 'test-js-optimizer-t3-output.js')).read(), - ['optimizeShiftsAggressive']), - (path_from_root('tools', 'test-js-optimizer-regs.js'), open(path_from_root('tools', 'test-js-optimizer-regs-output.js')).read(), - ['registerize']), - (path_from_root('tools', 'eliminator', 'eliminator-test.js'), open(path_from_root('tools', 'eliminator', 'eliminator-test-output.js')).read(), - ['eliminate']), - (path_from_root('tools', 'eliminator', 'safe-eliminator-test.js'), open(path_from_root('tools', 'eliminator', 'safe-eliminator-test-output.js')).read(), - ['eliminateMemSafe']), - (path_from_root('tools', 'eliminator', 'asm-eliminator-test.js'), open(path_from_root('tools', 'eliminator', 'asm-eliminator-test-output.js')).read(), - ['asm', 'eliminate']), - (path_from_root('tools', 'test-js-optimizer-asm-regs.js'), open(path_from_root('tools', 'test-js-optimizer-asm-regs-output.js')).read(), - ['asm', 'registerize']), - (path_from_root('tools', 'test-js-optimizer-asm-regs-min.js'), open(path_from_root('tools', 'test-js-optimizer-asm-regs-min-output.js')).read(), - ['asm', 'registerize']), - (path_from_root('tools', 'test-js-optimizer-asm-pre.js'), open(path_from_root('tools', 'test-js-optimizer-asm-pre-output.js')).read(), - ['asm', 'simplifyExpressions']), - (path_from_root('tools', 'test-js-optimizer-asm-last.js'), open(path_from_root('tools', 'test-js-optimizer-asm-last-output.js')).read(), - ['asm', 'last']), - (path_from_root('tools', 'test-js-optimizer-asm-relocate.js'), open(path_from_root('tools', 'test-js-optimizer-asm-relocate-output.js')).read(), - ['asm', 'relocate']), - (path_from_root('tools', 'test-js-optimizer-asm-outline1.js'), open(path_from_root('tools', 'test-js-optimizer-asm-outline1-output.js')).read(), - ['asm', 'outline']), - (path_from_root('tools', 'test-js-optimizer-asm-outline2.js'), open(path_from_root('tools', 'test-js-optimizer-asm-outline2-output.js')).read(), - ['asm', 'outline']), - (path_from_root('tools', 'test-js-optimizer-asm-outline3.js'), open(path_from_root('tools', 'test-js-optimizer-asm-outline3-output.js')).read(), - ['asm', 'outline']), - ]: - print input - output = Popen(listify(NODE_JS) + [path_from_root('tools', 'js-optimizer.js'), input] + passes, stdin=PIPE, stdout=PIPE).communicate()[0] - self.assertIdentical(expected, output.replace('\r\n', '\n').replace('\n\n', '\n')) - - def test_m_mm(self): - open(os.path.join(self.get_dir(), 'foo.c'), 'w').write('''#include ''') - for opt in ['M', 'MM']: - output, err = Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'foo.c'), '-' + opt], stdout=PIPE, stderr=PIPE).communicate() - assert 'foo.o: ' in output, '-%s failed to produce the right output: %s' % (opt, output) - assert 'error' not in err, 'Unexpected stderr: ' + err - - def test_chunking(self): - if os.environ.get('EMCC_DEBUG'): return self.skip('cannot run in debug mode') - if os.environ.get('EMCC_CORES'): return self.skip('cannot run if cores are altered') - if multiprocessing.cpu_count() < 2: return self.skip('need multiple cores') - try: - os.environ['EMCC_DEBUG'] = '1' - os.environ['EMCC_CORES'] = '2' - for asm, linkable, chunks, js_chunks in [ - (0, 0, 3, 2), (0, 1, 3, 4), - (1, 0, 3, 2), (1, 1, 3, 4) - ]: - print asm, linkable, chunks, js_chunks - output, err = Popen([PYTHON, EMCC, path_from_root('tests', 'hello_libcxx.cpp'), '-O1', '-s', 'LINKABLE=%d' % linkable, '-s', 'ASM_JS=%d' % asm] + (['-O2'] if asm else []), stdout=PIPE, stderr=PIPE).communicate() - ok = False - for c in range(chunks, chunks+2): - ok = ok or ('phase 2 working on %d chunks' % c in err) - assert ok, err - ok = False - for c in range(js_chunks, js_chunks+2): - ok = ok or ('splitting up js optimization into %d chunks' % c in err) - assert ok, err - finally: - del os.environ['EMCC_DEBUG'] - del os.environ['EMCC_CORES'] - - def test_debuginfo(self): - if os.environ.get('EMCC_DEBUG'): return self.skip('cannot run in debug mode') - try: - os.environ['EMCC_DEBUG'] = '1' - # llvm debug info is kept only when we can see it, which is without the js optimize, -O0. js debug info is lost by registerize in -O2, so - g disables it - for args, expect_llvm, expect_js in [ - (['-O0'], True, True), - (['-O0', '-g'], True, True), - (['-O1'], False, True), - (['-O1', '-g'], False, True), - (['-O2'], False, False), - (['-O2', '-g'], False, True), - ]: - print args, expect_llvm, expect_js - output, err = Popen([PYTHON, EMCC, path_from_root('tests', 'hello_world.cpp')] + args, stdout=PIPE, stderr=PIPE).communicate() - assert expect_llvm == ('strip-debug' not in err) - assert expect_js == ('registerize' not in err) - finally: - del os.environ['EMCC_DEBUG'] - - def test_scons(self): # also incidentally tests c++11 integration in llvm 3.1 - try_delete(os.path.join(self.get_dir(), 'test')) - shutil.copytree(path_from_root('tests', 'scons'), os.path.join(self.get_dir(), 'test')) - shutil.copytree(path_from_root('tools', 'scons', 'site_scons'), os.path.join(self.get_dir(), 'test', 'site_scons')) - os.chdir(os.path.join(self.get_dir(), 'test')) - Popen(['scons']).communicate() - output = run_js('scons_integration.js') - assert 'If you see this - the world is all right!' in output - - def test_embind(self): - for args, fail in [ - ([], True), # without --bind, we fail - (['--bind'], False), - (['--bind', '-O1'], False), - (['--bind', '-O2'], False), - (['--bind', '-O1', '-s', 'ASM_JS=0'], False), - (['--bind', '-O2', '-s', 'ASM_JS=0'], False) - ]: - print args, fail - self.clear() - try_delete(self.in_dir('a.out.js')) - Popen([PYTHON, EMCC, path_from_root('tests', 'embind', 'embind_test.cpp'), '--post-js', path_from_root('tests', 'embind', 'underscore-1.4.2.js'), '--post-js', path_from_root('tests', 'embind', 'imvu_test_adapter.js'), '--post-js', path_from_root('tests', 'embind', 'embind.test.js')] + args, stderr=PIPE if fail else None).communicate() - assert os.path.exists(self.in_dir('a.out.js')) == (not fail) - if not fail: - output = run_js(self.in_dir('a.out.js'), stdout=PIPE, stderr=PIPE, full_output=True) - assert "FAIL" not in output, output - - def test_llvm_nativizer(self): - try: - Popen(['as', '--version'], stdout=PIPE, stderr=PIPE).communicate() - except: - return self.skip('no gnu as, cannot run nativizer') - - # avoid impure_ptr problems etc. - shutil.copyfile(path_from_root('tests', 'files.cpp'), os.path.join(self.get_dir(), 'files.cpp')) - open(os.path.join(self.get_dir(), 'somefile.binary'), 'w').write('''waka waka############################''') - open(os.path.join(self.get_dir(), 'test.file'), 'w').write('''ay file..............,,,,,,,,,,,,,,''') - open(os.path.join(self.get_dir(), 'stdin'), 'w').write('''inter-active''') - Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'files.cpp'), '-c']).communicate() - Popen([PYTHON, path_from_root('tools', 'nativize_llvm.py'), os.path.join(self.get_dir(), 'files.o')], stdout=PIPE, stderr=PIPE).communicate(input) - output = Popen([os.path.join(self.get_dir(), 'files.o.run')], stdin=open(os.path.join(self.get_dir(), 'stdin')), stdout=PIPE, stderr=PIPE).communicate() - self.assertContained('''size: 37 -data: 119,97,107,97,32,119,97,107,97,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35 -loop: 119 97 107 97 32 119 97 107 97 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 -input:inter-active -texto -$ -5 : 10,30,20,11,88 -other=ay file... -seeked= file. -''', output[0]) - self.assertIdentical('texte\n', output[1]) - - def test_emconfig(self): - output = Popen([PYTHON, EMCONFIG, 'LLVM_ROOT'], stdout=PIPE, stderr=PIPE).communicate()[0].strip() - try: - assert output == LLVM_ROOT - except: - print >> sys.stderr, 'Assertion failed: python %s LLVM_ROOT returned "%s" instead of expected "%s"!' % (EMCONFIG, output, LLVM_ROOT) - raise - invalid = 'Usage: em-config VAR_NAME' - # Don't accept variables that do not exist - output = Popen([PYTHON, EMCONFIG, 'VAR_WHICH_DOES_NOT_EXIST'], stdout=PIPE, stderr=PIPE).communicate()[0].strip() - assert output == invalid - # Don't accept no arguments - output = Popen([PYTHON, EMCONFIG], stdout=PIPE, stderr=PIPE).communicate()[0].strip() - assert output == invalid - # Don't accept more than one variable - output = Popen([PYTHON, EMCONFIG, 'LLVM_ROOT', 'EMCC'], stdout=PIPE, stderr=PIPE).communicate()[0].strip() - assert output == invalid - # Don't accept arbitrary python code - output = Popen([PYTHON, EMCONFIG, 'sys.argv[1]'], stdout=PIPE, stderr=PIPE).communicate()[0].strip() - assert output == invalid - - def test_link_s(self): - # -s OPT=VALUE can conflict with -s as a linker option. We warn and ignore - open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write(r''' - extern "C" { - void something(); - } - - int main() { - something(); - return 0; - } - ''') - open(os.path.join(self.get_dir(), 'supp.cpp'), 'w').write(r''' - #include - - extern "C" { - void something() { - printf("yello\n"); - } - } - ''') - Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '-o', 'main.o']).communicate() - Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'supp.cpp'), '-o', 'supp.o']).communicate() - - output = Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.o'), '-s', os.path.join(self.get_dir(), 'supp.o'), '-s', 'SAFE_HEAP=1'], stderr=PIPE).communicate() - self.assertContained('treating -s as linker option', output[1]) - output = run_js('a.out.js') - assert 'yello' in output, 'code works' - code = open('a.out.js').read() - assert 'SAFE_HEAP' in code, 'valid -s option had an effect' - - def test_jcache_printf(self): - open(self.in_dir('src.cpp'), 'w').write(r''' - #include - #include - #include - int main() { - emscripten_jcache_printf("hello world\n"); - emscripten_jcache_printf("hello %d world\n", 5); - emscripten_jcache_printf("hello %.3f world\n", 123.456789123); - emscripten_jcache_printf("hello %llx world\n", 0x1234567811223344ULL); - return 0; - } - ''') - Popen([PYTHON, EMCC, self.in_dir('src.cpp')]).communicate() - output = run_js('a.out.js') - self.assertIdentical('hello world\nhello 5 world\nhello 123.457 world\nhello 1234567811223300 world\n', output) - - def test_conftest_s_flag_passing(self): - open(os.path.join(self.get_dir(), 'conftest.c'), 'w').write(r''' - int main() { - return 0; - } - ''') - os.environ["EMMAKEN_JUST_CONFIGURE"] = "1" - cmd = [PYTHON, EMCC, '-s', 'ASSERTIONS=1', os.path.join(self.get_dir(), 'conftest.c'), '-o', 'conftest'] - output = Popen(cmd, stderr=PIPE).communicate() - del os.environ["EMMAKEN_JUST_CONFIGURE"] - self.assertNotContained('emcc: warning: treating -s as linker option', output[1]) - assert os.path.exists('conftest') - - def test_file_packager(self): - try: - os.mkdir('subdir') - except: - pass - open('data1.txt', 'w').write('data1') - os.chdir('subdir') - open('data2.txt', 'w').write('data2') - # relative path to below the current dir is invalid - out, err = Popen([PYTHON, FILE_PACKAGER, 'test.data', '--preload', '../data1.txt'], stdout=PIPE, stderr=PIPE).communicate() - assert len(out) == 0 - assert 'below the current directory' in err - # relative path that ends up under us is cool - out, err = Popen([PYTHON, FILE_PACKAGER, 'test.data', '--preload', '../subdir/data2.txt'], stdout=PIPE, stderr=PIPE).communicate() - assert len(out) > 0 - assert 'below the current directory' not in err - # direct path leads to the same code being generated - relative path does not make us do anything different - out2, err2 = Popen([PYTHON, FILE_PACKAGER, 'test.data', '--preload', 'data2.txt'], stdout=PIPE, stderr=PIPE).communicate() - assert len(out2) > 0 - assert 'below the current directory' not in err2 - def clean(txt): - return filter(lambda line: 'PACKAGE_UUID' not in line, txt.split('\n')) - out = clean(out) - out2 = clean(out2) - assert out == out2 - # sanity check that we do generate different code for different inputs - out3, err3 = Popen([PYTHON, FILE_PACKAGER, 'test.data', '--preload', 'data2.txt', 'data2.txt@waka.txt'], stdout=PIPE, stderr=PIPE).communicate() - out3 = clean(out3) - assert out != out3 - - def test_crunch(self): - # crunch should not be run if a .crn exists that is more recent than the .dds - shutil.copyfile(path_from_root('tests', 'ship.dds'), 'ship.dds') - time.sleep(0.1) - Popen([PYTHON, FILE_PACKAGER, 'test.data', '--pre-run', '--crunch=32', '--preload', 'ship.dds'], stdout=open('pre.js', 'w')).communicate() - assert os.stat('test.data').st_size < 0.25*os.stat('ship.dds').st_size, 'Compressed should be much smaller than dds' - crunch_time = os.stat('ship.crn').st_mtime - dds_time = os.stat('ship.dds').st_mtime - assert crunch_time > dds_time, 'Crunch is more recent' - # run again, should not recrunch! - time.sleep(0.1) - Popen([PYTHON, FILE_PACKAGER, 'test.data', '--pre-run', '--crunch=32', '--preload', 'ship.dds'], stdout=open('pre.js', 'w')).communicate() - assert crunch_time == os.stat('ship.crn').st_mtime, 'Crunch is unchanged' - # update dds, so should recrunch - time.sleep(0.1) - os.utime('ship.dds', None) - Popen([PYTHON, FILE_PACKAGER, 'test.data', '--pre-run', '--crunch=32', '--preload', 'ship.dds'], stdout=open('pre.js', 'w')).communicate() - assert crunch_time < os.stat('ship.crn').st_mtime, 'Crunch was changed' - - def test_headless(self): - if SPIDERMONKEY_ENGINE not in JS_ENGINES: return self.skip('cannot run without spidermonkey due to node limitations (Uint8ClampedArray etc.)') - - shutil.copyfile(path_from_root('tests', 'screenshot.png'), os.path.join(self.get_dir(), 'example.png')) - Popen([PYTHON, EMCC, path_from_root('tests', 'sdl_canvas.c'), '-s', 'HEADLESS=1']).communicate() - output = run_js('a.out.js', engine=SPIDERMONKEY_ENGINE, stderr=PIPE) - assert '''Init: 0 -Font: 0x1 -Sum: 0 -you should see two lines of text in different colors and a blue rectangle -SDL_Quit called (and ignored) -done. -''' in output, output - -elif 'browser' in str(sys.argv): - # Browser tests. - - ''' Enable this code to run in another browser than webbrowser detects as default - def run_in_other_browser(url): - execute(['yourbrowser', url]) - webbrowser.open_new = run_in_other_browser - ''' - - print - print 'Running the browser tests. Make sure the browser allows popups from localhost.' - print - - if 'audio' in sys.argv: - print - print 'Running the browser audio tests. Make sure to listen to hear the correct results!' - print - - i = sys.argv.index('audio') - sys.argv = sys.argv[:i] + sys.argv[i+1:] - i = sys.argv.index('browser') - sys.argv = sys.argv[:i] + sys.argv[i+1:] - sys.argv += [ - 'browser.test_sdl_audio', - 'browser.test_sdl_audio_mix_channels', - 'browser.test_sdl_audio_mix', - 'browser.test_sdl_audio_quickload', - 'browser.test_openal_playback', - 'browser.test_openal_buffers', - 'browser.test_freealut' - ] - - if 'sockets' in sys.argv: - print - print 'Running the browser socket tests.' - print - i = sys.argv.index('sockets') - sys.argv = sys.argv[:i] + sys.argv[i+1:] - i = sys.argv.index('browser') - sys.argv = sys.argv[:i] + sys.argv[i+1:] - sys.argv += [ - 'browser.test_sockets_echo', - 'browser.test_sockets_echo_bigdata', - 'browser.test_sockets_partial', - 'browser.test_sockets_select_server_no_accept', - 'browser.test_sockets_select_server_closes_connection_rw', - 'browser.test_enet' - ] - - # Run a server and a web page. When a test runs, we tell the server about it, - # which tells the web page, which then opens a window with the test. Doing - # it this way then allows the page to close() itself when done. - - def harness_server_func(q): - class TestServerHandler(BaseHTTPServer.BaseHTTPRequestHandler): - def do_GET(s): - s.send_response(200) - s.send_header("Content-type", "text/html") - s.end_headers() - if s.path == '/run_harness': - s.wfile.write(open(path_from_root('tests', 'browser_harness.html')).read()) - else: - result = 'False' - if not q.empty(): - result = q.get() - s.wfile.write(result) - s.wfile.close() - def log_request(code=0, size=0): - # don't log; too noisy - pass - httpd = BaseHTTPServer.HTTPServer(('localhost', 9999), TestServerHandler) - httpd.serve_forever() # test runner will kill us - - def server_func(dir, q): - class TestServerHandler(BaseHTTPServer.BaseHTTPRequestHandler): - def do_GET(s): - if 'report_' in s.path: - q.put(s.path) - else: - filename = s.path[1:] - if os.path.exists(filename): - s.send_response(200) - s.send_header("Content-type", "text/html") - s.end_headers() - s.wfile.write(open(filename).read()) - s.wfile.close() - else: - s.send_response(500) - s.send_header("Content-type", "text/html") - s.end_headers() - def log_request(code=0, size=0): - # don't log; too noisy - pass - os.chdir(dir) - httpd = BaseHTTPServer.HTTPServer(('localhost', 8888), TestServerHandler) - httpd.serve_forever() # test runner will kill us - - class browser(RunnerCore): - def __init__(self, *args, **kwargs): - super(browser, self).__init__(*args, **kwargs) - - if hasattr(browser, 'harness_server'): return - browser.harness_queue = multiprocessing.Queue() - browser.harness_server = multiprocessing.Process(target=harness_server_func, args=(browser.harness_queue,)) - browser.harness_server.start() - print '[Browser harness server on process %d]' % browser.harness_server.pid - webbrowser.open_new('http://localhost:9999/run_harness') - - @classmethod - def tearDownClass(cls): - if not hasattr(browser, 'harness_server'): return - - browser.harness_server.terminate() - delattr(browser, 'harness_server') - print '[Browser harness server terminated]' - # On Windows, shutil.rmtree() in tearDown() raises this exception if we do not wait a bit: - # WindowsError: [Error 32] The process cannot access the file because it is being used by another process. - time.sleep(0.1) - - def run_browser(self, html_file, message, expectedResult=None): - if expectedResult is not None: - try: - queue = multiprocessing.Queue() - server = multiprocessing.Process(target=functools.partial(server_func, self.get_dir()), args=(queue,)) - server.start() - browser.harness_queue.put('http://localhost:8888/' + html_file) - output = '[no http server activity]' - start = time.time() - while time.time() - start < 60: - if not queue.empty(): - output = queue.get() - break - time.sleep(0.1) - - self.assertIdentical(expectedResult, output) - finally: - server.terminate() - time.sleep(0.1) # see comment about Windows above - else: - webbrowser.open_new(os.path.abspath(html_file)) - print 'A web browser window should have opened a page containing the results of a part of this test.' - print 'You need to manually look at the page to see that it works ok: ' + message - print '(sleeping for a bit to keep the directory alive for the web browser..)' - time.sleep(5) - print '(moving on..)' - - def with_report_result(self, code): - return r''' - #if EMSCRIPTEN - #include - #define REPORT_RESULT_INTERNAL(sync) \ - char output[1000]; \ - sprintf(output, \ - "xhr = new XMLHttpRequest();" \ - "xhr.open('GET', 'http://localhost:8888/report_result?%d'%s);" \ - "xhr.send();", result, sync ? ", false" : ""); \ - emscripten_run_script(output); \ - emscripten_run_script("setTimeout(function() { window.close() }, 1000)"); // comment this out to keep the test runner window open to debug - #define REPORT_RESULT() REPORT_RESULT_INTERNAL(0) - #endif -''' + code - - def reftest(self, expected): - # make sure the pngs used here have no color correction, using e.g. - # pngcrush -rem gAMA -rem cHRM -rem iCCP -rem sRGB infile outfile - basename = os.path.basename(expected) - shutil.copyfile(expected, os.path.join(self.get_dir(), basename)) - open(os.path.join(self.get_dir(), 'reftest.js'), 'w').write(''' - var Module = eval('Module'); - function doReftest() { - if (doReftest.done) return; - doReftest.done = true; - var img = new Image(); - img.onload = function() { - assert(img.width == Module.canvas.width, 'Invalid width: ' + Module.canvas.width + ', should be ' + img.width); - assert(img.height == Module.canvas.height, 'Invalid height: ' + Module.canvas.height + ', should be ' + img.height); - - var canvas = document.createElement('canvas'); - canvas.width = img.width; - canvas.height = img.height; - var ctx = canvas.getContext('2d'); - ctx.drawImage(img, 0, 0); - var expected = ctx.getImageData(0, 0, img.width, img.height).data; - - var actualUrl = Module.canvas.toDataURL(); - var actualImage = new Image(); - actualImage.onload = function() { - /* - document.body.appendChild(img); // for comparisons - var div = document.createElement('div'); - div.innerHTML = '^=expected, v=actual'; - document.body.appendChild(div); - document.body.appendChild(actualImage); // to grab it for creating the test reference - */ - - var actualCanvas = document.createElement('canvas'); - actualCanvas.width = actualImage.width; - actualCanvas.height = actualImage.height; - var actualCtx = actualCanvas.getContext('2d'); - actualCtx.drawImage(actualImage, 0, 0); - var actual = actualCtx.getImageData(0, 0, actualImage.width, actualImage.height).data; - - var total = 0; - var width = img.width; - var height = img.height; - for (var x = 0; x < width; x++) { - for (var y = 0; y < height; y++) { - total += Math.abs(expected[y*width*4 + x*4 + 0] - actual[y*width*4 + x*4 + 0]); - total += Math.abs(expected[y*width*4 + x*4 + 1] - actual[y*width*4 + x*4 + 1]); - total += Math.abs(expected[y*width*4 + x*4 + 2] - actual[y*width*4 + x*4 + 2]); - } - } - var wrong = Math.floor(total / (img.width*img.height*3)); // floor, to allow some margin of error for antialiasing - - xhr = new XMLHttpRequest(); - xhr.open('GET', 'http://localhost:8888/report_result?' + wrong); - xhr.send(); - setTimeout(function() { window.close() }, 1000); - }; - actualImage.src = actualUrl; - } - img.src = '%s'; - }; - Module['postRun'] = doReftest; - Module['preRun'].push(function() { - setTimeout(doReftest, 1000); // if run() throws an exception and postRun is not called, this will kick in - }); -''' % basename) - - def test_html(self): - # test HTML generation. - self.btest('hello_world_sdl.cpp', reference='htmltest.png', - message='You should see "hello, world!" and a colored cube.') - - def test_html_source_map(self): - if 'test_html_source_map' not in str(sys.argv): return self.skip('''This test - requires manual intervention; will not be run unless explicitly requested''') - cpp_file = os.path.join(self.get_dir(), 'src.cpp') - html_file = os.path.join(self.get_dir(), 'src.html') - # browsers will try to 'guess' the corresponding original line if a - # generated line is unmapped, so if we want to make sure that our - # numbering is correct, we need to provide a couple of 'possible wrong - # answers'. thus, we add some printf calls so that the cpp file gets - # multiple mapped lines. in other words, if the program consists of a - # single 'throw' statement, browsers may just map any thrown exception to - # that line, because it will be the only mapped line. - with open(cpp_file, 'w') as f: - f.write(r''' - #include - - int main() { - printf("Starting test\n"); - try { - throw 42; // line 8 - } catch (int e) { } - printf("done\n"); - return 0; - } - ''') - # use relative paths when calling emcc, because file:// URIs can only load - # sourceContent when the maps are relative paths - Popen([PYTHON, EMCC, 'src.cpp', '-o', 'src.html', '-g4'], - cwd=self.get_dir()).communicate() - webbrowser.open_new('file://' + html_file) - print ''' -Set the debugger to pause on exceptions -You should see an exception thrown at src.cpp:7. -Press any key to continue.''' - raw_input() - - def build_native_lzma(self): - lzma_native = path_from_root('third_party', 'lzma.js', 'lzma-native') - if os.path.isfile(lzma_native) and os.access(lzma_native, os.X_OK): return - - cwd = os.getcwd() - try: - os.chdir(path_from_root('third_party', 'lzma.js')) - Popen(['sh', './doit.sh']).communicate() - finally: - os.chdir(cwd) - - def test_split(self): - # test HTML generation. - self.reftest(path_from_root('tests', 'htmltest.png')) - output = Popen([PYTHON, EMCC, path_from_root('tests', 'hello_world_sdl.cpp'), '-o', 'something.js', '--split', '100', '--pre-js', 'reftest.js']).communicate() - assert os.path.exists(os.path.join(self.get_dir(), 'something.js')), 'must be main js file' - assert os.path.exists(os.path.join(self.get_dir(), 'something_functions.js')), 'must be functions js file' - assert os.path.exists(os.path.join(self.get_dir(), 'something.include.html')), 'must be js include file' - - open(os.path.join(self.get_dir(), 'something.html'), 'w').write(''' - - - - - - - Emscripten-Generated Code - - - -
-
Downloading...
-
- -
- -
-
-
- -
- ''' + open(os.path.join(self.get_dir(), 'something.include.html')).read() + ''' - - - ''') - - self.run_browser('something.html', 'You should see "hello, world!" and a colored cube.', '/report_result?0') - - def test_split_in_source_filenames(self): - self.reftest(path_from_root('tests', 'htmltest.png')) - output = Popen([PYTHON, EMCC, path_from_root('tests', 'hello_world_sdl.cpp'), '-o', 'something.js', '-g', '--split', '100', '--pre-js', 'reftest.js']).communicate() - assert os.path.exists(os.path.join(self.get_dir(), 'something.js')), 'must be main js file' - assert os.path.exists(self.get_dir() + '/something/' + path_from_root('tests', 'hello_world_sdl.cpp.js')), 'must be functions js file' - assert os.path.exists(os.path.join(self.get_dir(), 'something.include.html')), 'must be js include file' - - open(os.path.join(self.get_dir(), 'something.html'), 'w').write(''' - - - - - - - Emscripten-Generated Code - - - -
-
Downloading...
-
- -
- -
-
-
- -
- ''' + open(os.path.join(self.get_dir(), 'something.include.html')).read() + ''' - - - ''') - - self.run_browser('something.html', 'You should see "hello, world!" and a colored cube.', '/report_result?0') - - def test_compression(self): - open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write(self.with_report_result(r''' - #include - #include - int main() { - printf("hello compressed world\n"); - int result = 1; - REPORT_RESULT(); - return 0; - } - ''')) - - self.build_native_lzma() - Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '-o', 'page.html', - '--compression', '%s,%s,%s' % (path_from_root('third_party', 'lzma.js', 'lzma-native'), - path_from_root('third_party', 'lzma.js', 'lzma-decoder.js'), - 'LZMA.decompress')]).communicate() - assert os.path.exists(os.path.join(self.get_dir(), 'page.js')), 'must be side js' - assert os.path.exists(os.path.join(self.get_dir(), 'page.js.compress')), 'must be side compressed js' - assert os.stat(os.path.join(self.get_dir(), 'page.js')).st_size > os.stat(os.path.join(self.get_dir(), 'page.js.compress')).st_size, 'compressed file must be smaller' - shutil.move(os.path.join(self.get_dir(), 'page.js'), 'page.js.renamedsoitcannotbefound'); - self.run_browser('page.html', '', '/report_result?1') - - def test_preload_file(self): - absolute_src_path = os.path.join(self.get_dir(), 'somefile.txt').replace('\\', '/') - open(absolute_src_path, 'w').write('''load me right before running the code please''') - - absolute_src_path2 = os.path.join(self.get_dir(), '.somefile.txt').replace('\\', '/') - open(absolute_src_path2, 'w').write('''load me right before running the code please''') - - def make_main(path): - print 'make main at', path - open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write(self.with_report_result(r''' - #include - #include - #include - int main() { - FILE *f = fopen("%s", "r"); - char buf[100]; - fread(buf, 1, 20, f); - buf[20] = 0; - fclose(f); - printf("|%%s|\n", buf); - - int result = !strcmp("load me right before", buf); - REPORT_RESULT(); - return 0; - } - ''' % path)) - - test_cases = [ - # (source preload-file string, file on target FS to load) - ("somefile.txt", "somefile.txt"), - (".somefile.txt@somefile.txt", "somefile.txt"), - ("./somefile.txt", "somefile.txt"), - ("somefile.txt@file.txt", "file.txt"), - ("./somefile.txt@file.txt", "file.txt"), - ("./somefile.txt@./file.txt", "file.txt"), - ("somefile.txt@/file.txt", "file.txt"), - ("somefile.txt@/", "somefile.txt"), - (absolute_src_path + "@file.txt", "file.txt"), - (absolute_src_path + "@/file.txt", "file.txt"), - (absolute_src_path + "@/", "somefile.txt"), - ("somefile.txt@/directory/file.txt", "/directory/file.txt"), - ("somefile.txt@/directory/file.txt", "directory/file.txt"), - (absolute_src_path + "@/directory/file.txt", "directory/file.txt")] - - for test in test_cases: - (srcpath, dstpath) = test - print 'Testing', srcpath, dstpath - make_main(dstpath) - Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--preload-file', srcpath, '-o', 'page.html']).communicate() - self.run_browser('page.html', 'You should see |load me right before|.', '/report_result?1') - - # By absolute path - - make_main('somefile.txt') # absolute becomes relative - Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--preload-file', absolute_src_path, '-o', 'page.html']).communicate() - self.run_browser('page.html', 'You should see |load me right before|.', '/report_result?1') - - # Test subdirectory handling with asset packaging. - os.makedirs(os.path.join(self.get_dir(), 'assets/sub/asset1/').replace('\\', '/')) - os.makedirs(os.path.join(self.get_dir(), 'assets/sub/asset1/.git').replace('\\', '/')) # Test adding directory that shouldn't exist. - os.makedirs(os.path.join(self.get_dir(), 'assets/sub/asset2/').replace('\\', '/')) - open(os.path.join(self.get_dir(), 'assets/sub/asset1/file1.txt'), 'w').write('''load me right before running the code please''') - open(os.path.join(self.get_dir(), 'assets/sub/asset1/.git/shouldnt_be_embedded.txt'), 'w').write('''this file should not get embedded''') - open(os.path.join(self.get_dir(), 'assets/sub/asset2/file2.txt'), 'w').write('''load me right before running the code please''') - absolute_assets_src_path = os.path.join(self.get_dir(), 'assets').replace('\\', '/') - def make_main_two_files(path1, path2, nonexistingpath): - open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write(self.with_report_result(r''' - #include - #include - #include - int main() { - FILE *f = fopen("%s", "r"); - char buf[100]; - fread(buf, 1, 20, f); - buf[20] = 0; - fclose(f); - printf("|%%s|\n", buf); - - int result = !strcmp("load me right before", buf); - - f = fopen("%s", "r"); - if (f == NULL) - result = 0; - fclose(f); - - f = fopen("%s", "r"); - if (f != NULL) - result = 0; - - REPORT_RESULT(); - return 0; - } - ''' % (path1, path2, nonexistingpath))) - - test_cases = [ - # (source directory to embed, file1 on target FS to load, file2 on target FS to load, name of a file that *shouldn't* exist on VFS) - ("assets", "assets/sub/asset1/file1.txt", "assets/sub/asset2/file2.txt", "assets/sub/asset1/.git/shouldnt_be_embedded.txt"), - ("assets/", "assets/sub/asset1/file1.txt", "assets/sub/asset2/file2.txt", "assets/sub/asset1/.git/shouldnt_be_embedded.txt"), - ("assets@/", "/sub/asset1/file1.txt", "/sub/asset2/file2.txt", "/sub/asset1/.git/shouldnt_be_embedded.txt"), - ("assets/@/", "/sub/asset1/file1.txt", "/sub/asset2/file2.txt", "/sub/asset1/.git/shouldnt_be_embedded.txt"), - ("assets@./", "/sub/asset1/file1.txt", "/sub/asset2/file2.txt", "/sub/asset1/.git/shouldnt_be_embedded.txt"), - (absolute_assets_src_path + "@/", "/sub/asset1/file1.txt", "/sub/asset2/file2.txt", "/sub/asset1/.git/shouldnt_be_embedded.txt"), - (absolute_assets_src_path + "@/assets", "/assets/sub/asset1/file1.txt", "/assets/sub/asset2/file2.txt", "assets/sub/asset1/.git/shouldnt_be_embedded.txt")] - - for test in test_cases: - (srcpath, dstpath1, dstpath2, nonexistingpath) = test - make_main_two_files(dstpath1, dstpath2, nonexistingpath) - print srcpath - Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--preload-file', srcpath, '-o', 'page.html']).communicate() - self.run_browser('page.html', 'You should see |load me right before|.', '/report_result?1') - - # Should still work with -o subdir/.. - - make_main('somefile.txt') # absolute becomes relative - try: - os.mkdir(os.path.join(self.get_dir(), 'dirrey')) - except: - pass - Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--preload-file', absolute_src_path, '-o', 'dirrey/page.html']).communicate() - self.run_browser('dirrey/page.html', 'You should see |load me right before|.', '/report_result?1') - - # With FS.preloadFile - - open(os.path.join(self.get_dir(), 'pre.js'), 'w').write(''' - Module.preRun = function() { - FS.createPreloadedFile('/', 'someotherfile.txt', 'somefile.txt', true, false); - }; - ''') - make_main('someotherfile.txt') - Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--pre-js', 'pre.js', '-o', 'page.html']).communicate() - self.run_browser('page.html', 'You should see |load me right before|.', '/report_result?1') - - def test_preload_caching(self): - open(os.path.join(self.get_dir(), 'somefile.txt'), 'w').write('''load me right before running the code please''') - def make_main(path): - print path - open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write(self.with_report_result(r''' - #include - #include - #include - - extern "C" { - extern int checkPreloadResults(); - } - - int main(int argc, char** argv) { - FILE *f = fopen("%s", "r"); - char buf[100]; - fread(buf, 1, 20, f); - buf[20] = 0; - fclose(f); - printf("|%%s|\n", buf); - - int result = 0; - - result += !strcmp("load me right before", buf); - result += checkPreloadResults(); - - REPORT_RESULT(); - return 0; - } - ''' % path)) - - open(os.path.join(self.get_dir(), 'test.js'), 'w').write(''' - mergeInto(LibraryManager.library, { - checkPreloadResults: function() { - var cached = 0; - var packages = Object.keys(Module['preloadResults']); - packages.forEach(function(package) { - var fromCache = Module['preloadResults'][package]['fromCache']; - if (fromCache) - ++ cached; - }); - return cached; - } - }); - ''') - - make_main('somefile.txt') - Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--use-preload-cache', '--js-library', os.path.join(self.get_dir(), 'test.js'), '--preload-file', 'somefile.txt', '-o', 'page.html']).communicate() - self.run_browser('page.html', 'You should see |load me right before|.', '/report_result?1') - self.run_browser('page.html', 'You should see |load me right before|.', '/report_result?2') - - def test_multifile(self): - # a few files inside a directory - self.clear() - os.makedirs(os.path.join(self.get_dir(), 'subdirr')); - os.makedirs(os.path.join(self.get_dir(), 'subdirr', 'moar')); - open(os.path.join(self.get_dir(), 'subdirr', 'data1.txt'), 'w').write('''1214141516171819''') - open(os.path.join(self.get_dir(), 'subdirr', 'moar', 'data2.txt'), 'w').write('''3.14159265358979''') - open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write(self.with_report_result(r''' - #include - #include - #include - int main() { - char buf[17]; - - FILE *f = fopen("subdirr/data1.txt", "r"); - fread(buf, 1, 16, f); - buf[16] = 0; - fclose(f); - printf("|%s|\n", buf); - int result = !strcmp("1214141516171819", buf); - - FILE *f2 = fopen("subdirr/moar/data2.txt", "r"); - fread(buf, 1, 16, f2); - buf[16] = 0; - fclose(f2); - printf("|%s|\n", buf); - result = result && !strcmp("3.14159265358979", buf); - - REPORT_RESULT(); - return 0; - } - ''')) - - # by individual files - Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--preload-file', 'subdirr/data1.txt', '--preload-file', 'subdirr/moar/data2.txt', '-o', 'page.html']).communicate() - self.run_browser('page.html', 'You should see two cool numbers', '/report_result?1') - os.remove('page.html') - - # by directory, and remove files to make sure - Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--preload-file', 'subdirr', '-o', 'page.html']).communicate() - shutil.rmtree(os.path.join(self.get_dir(), 'subdirr')) - self.run_browser('page.html', 'You should see two cool numbers', '/report_result?1') - - def test_compressed_file(self): - open(os.path.join(self.get_dir(), 'datafile.txt'), 'w').write('compress this please' + (2000*'.')) - open(os.path.join(self.get_dir(), 'datafile2.txt'), 'w').write('moar' + (100*'!')) - open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write(self.with_report_result(r''' - #include - #include - #include - int main() { - char buf[21]; - FILE *f = fopen("datafile.txt", "r"); - fread(buf, 1, 20, f); - buf[20] = 0; - fclose(f); - printf("file says: |%s|\n", buf); - int result = !strcmp("compress this please", buf); - FILE *f2 = fopen("datafile2.txt", "r"); - fread(buf, 1, 5, f2); - buf[5] = 0; - fclose(f2); - result = result && !strcmp("moar!", buf); - printf("file 2 says: |%s|\n", buf); - REPORT_RESULT(); - return 0; - } - ''')) - - self.build_native_lzma() - Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '-o', 'page.html', '--preload-file', 'datafile.txt', '--preload-file', 'datafile2.txt', - '--compression', '%s,%s,%s' % (path_from_root('third_party', 'lzma.js', 'lzma-native'), - path_from_root('third_party', 'lzma.js', 'lzma-decoder.js'), - 'LZMA.decompress')]).communicate() - assert os.path.exists(os.path.join(self.get_dir(), 'datafile.txt')), 'must be data file' - assert os.path.exists(os.path.join(self.get_dir(), 'page.data.compress')), 'must be data file in compressed form' - assert os.stat(os.path.join(self.get_dir(), 'page.js')).st_size != os.stat(os.path.join(self.get_dir(), 'page.js.compress')).st_size, 'compressed file must be different' - shutil.move(os.path.join(self.get_dir(), 'datafile.txt'), 'datafile.txt.renamedsoitcannotbefound'); - self.run_browser('page.html', '', '/report_result?1') - - def test_sdl_image(self): - # load an image file, get pixel data. Also O2 coverage for --preload-file, and memory-init - shutil.copyfile(path_from_root('tests', 'screenshot.jpg'), os.path.join(self.get_dir(), 'screenshot.jpg')) - open(os.path.join(self.get_dir(), 'sdl_image.c'), 'w').write(self.with_report_result(open(path_from_root('tests', 'sdl_image.c')).read())) - - for mem in [0, 1]: - for dest, dirname, basename in [('screenshot.jpg', '/', 'screenshot.jpg'), - ('screenshot.jpg@/assets/screenshot.jpg', '/assets', 'screenshot.jpg')]: - Popen([ - PYTHON, EMCC, os.path.join(self.get_dir(), 'sdl_image.c'), '-o', 'page.html', '-O2', '--memory-init-file', str(mem), - '--preload-file', dest, '-DSCREENSHOT_DIRNAME="' + dirname + '"', '-DSCREENSHOT_BASENAME="' + basename + '"' - ]).communicate() - self.run_browser('page.html', '', '/report_result?600') - - def test_sdl_image_jpeg(self): - shutil.copyfile(path_from_root('tests', 'screenshot.jpg'), os.path.join(self.get_dir(), 'screenshot.jpeg')) - open(os.path.join(self.get_dir(), 'sdl_image_jpeg.c'), 'w').write(self.with_report_result(open(path_from_root('tests', 'sdl_image.c')).read())) - Popen([ - PYTHON, EMCC, os.path.join(self.get_dir(), 'sdl_image_jpeg.c'), '-o', 'page.html', - '--preload-file', 'screenshot.jpeg', '-DSCREENSHOT_DIRNAME="/"', '-DSCREENSHOT_BASENAME="screenshot.jpeg"' - ]).communicate() - self.run_browser('page.html', '', '/report_result?600') - - def test_sdl_image_compressed(self): - for image, width in [(path_from_root('tests', 'screenshot2.png'), 300), - (path_from_root('tests', 'screenshot.jpg'), 600)]: - self.clear() - print image - - basename = os.path.basename(image) - shutil.copyfile(image, os.path.join(self.get_dir(), basename)) - open(os.path.join(self.get_dir(), 'sdl_image.c'), 'w').write(self.with_report_result(open(path_from_root('tests', 'sdl_image.c')).read())) - - self.build_native_lzma() - Popen([ - PYTHON, EMCC, os.path.join(self.get_dir(), 'sdl_image.c'), '-o', 'page.html', - '--preload-file', basename, '-DSCREENSHOT_DIRNAME="/"', '-DSCREENSHOT_BASENAME="' + basename + '"', - '--compression', '%s,%s,%s' % (path_from_root('third_party', 'lzma.js', 'lzma-native'), - path_from_root('third_party', 'lzma.js', 'lzma-decoder.js'), - 'LZMA.decompress') - ]).communicate() - shutil.move(os.path.join(self.get_dir(), basename), basename + '.renamedsoitcannotbefound'); - self.run_browser('page.html', '', '/report_result?' + str(width)) - - def test_sdl_image_prepare(self): - # load an image file, get pixel data. - shutil.copyfile(path_from_root('tests', 'screenshot.jpg'), os.path.join(self.get_dir(), 'screenshot.not')) - self.btest('sdl_image_prepare.c', reference='screenshot.jpg', args=['--preload-file', 'screenshot.not']) - - def test_sdl_image_prepare_data(self): - # load an image file, get pixel data. - shutil.copyfile(path_from_root('tests', 'screenshot.jpg'), os.path.join(self.get_dir(), 'screenshot.not')) - self.btest('sdl_image_prepare_data.c', reference='screenshot.jpg', args=['--preload-file', 'screenshot.not']) - - def test_sdl_stb_image(self): - # load an image file, get pixel data. - shutil.copyfile(path_from_root('tests', 'screenshot.jpg'), os.path.join(self.get_dir(), 'screenshot.not')) - self.btest('sdl_stb_image.c', reference='screenshot.jpg', args=['-s', 'STB_IMAGE=1', '--preload-file', 'screenshot.not']) - - def test_sdl_stb_image_data(self): - # load an image file, get pixel data. - shutil.copyfile(path_from_root('tests', 'screenshot.jpg'), os.path.join(self.get_dir(), 'screenshot.not')) - self.btest('sdl_stb_image_data.c', reference='screenshot.jpg', args=['-s', 'STB_IMAGE=1', '--preload-file', 'screenshot.not']) - - def test_sdl_canvas(self): - open(os.path.join(self.get_dir(), 'sdl_canvas.c'), 'w').write(self.with_report_result(open(path_from_root('tests', 'sdl_canvas.c')).read())) - - Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'sdl_canvas.c'), '-o', 'page.html']).communicate() - self.run_browser('page.html', '', '/report_result?1') - - def test_sdl_key(self): - open(os.path.join(self.get_dir(), 'pre.js'), 'w').write(''' - Module.postRun = function() { - function doOne() { - Module._one(); - setTimeout(doOne, 1000/60); - } - setTimeout(doOne, 1000/60); - } - - function keydown(c) { - var event = document.createEvent("KeyboardEvent"); - event.initKeyEvent("keydown", true, true, window, - 0, 0, 0, 0, - c, c); - document.dispatchEvent(event); - } - - function keyup(c) { - var event = document.createEvent("KeyboardEvent"); - event.initKeyEvent("keyup", true, true, window, - 0, 0, 0, 0, - c, c); - document.dispatchEvent(event); - } - ''') - open(os.path.join(self.get_dir(), 'sdl_key.c'), 'w').write(self.with_report_result(open(path_from_root('tests', 'sdl_key.c')).read())) - - Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'sdl_key.c'), '-o', 'page.html', '--pre-js', 'pre.js', '-s', '''EXPORTED_FUNCTIONS=['_main', '_one']''']).communicate() - self.run_browser('page.html', '', '/report_result?223092870') - - def test_sdl_text(self): - open(os.path.join(self.get_dir(), 'pre.js'), 'w').write(''' - Module.postRun = function() { - function doOne() { - Module._one(); - setTimeout(doOne, 1000/60); - } - setTimeout(doOne, 1000/60); - } - - function simulateKeyEvent(charCode) { - var event = document.createEvent("KeyboardEvent"); - event.initKeyEvent("keypress", true, true, window, - 0, 0, 0, 0, 0, charCode); - document.body.dispatchEvent(event); - } - ''') - open(os.path.join(self.get_dir(), 'sdl_text.c'), 'w').write(self.with_report_result(open(path_from_root('tests', 'sdl_text.c')).read())) - - Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'sdl_text.c'), '-o', 'page.html', '--pre-js', 'pre.js', '-s', '''EXPORTED_FUNCTIONS=['_main', '_one']''']).communicate() - self.run_browser('page.html', '', '/report_result?1') - - def test_sdl_mouse(self): - open(os.path.join(self.get_dir(), 'pre.js'), 'w').write(''' - function simulateMouseEvent(x, y, button) { - var event = document.createEvent("MouseEvents"); - if (button >= 0) { - var event1 = document.createEvent("MouseEvents"); - event1.initMouseEvent('mousedown', true, true, window, - 1, Module['canvas'].offsetLeft + x, Module['canvas'].offsetTop + y, Module['canvas'].offsetLeft + x, Module['canvas'].offsetTop + y, - 0, 0, 0, 0, - button, null); - Module['canvas'].dispatchEvent(event1); - var event2 = document.createEvent("MouseEvents"); - event2.initMouseEvent('mouseup', true, true, window, - 1, Module['canvas'].offsetLeft + x, Module['canvas'].offsetTop + y, Module['canvas'].offsetLeft + x, Module['canvas'].offsetTop + y, - 0, 0, 0, 0, - button, null); - Module['canvas'].dispatchEvent(event2); - } else { - var event1 = document.createEvent("MouseEvents"); - event1.initMouseEvent('mousemove', true, true, window, - 0, Module['canvas'].offsetLeft + x, Module['canvas'].offsetTop + y, Module['canvas'].offsetLeft + x, Module['canvas'].offsetTop + y, - 0, 0, 0, 0, - 0, null); - Module['canvas'].dispatchEvent(event1); - } - } - window['simulateMouseEvent'] = simulateMouseEvent; - ''') - open(os.path.join(self.get_dir(), 'sdl_mouse.c'), 'w').write(self.with_report_result(open(path_from_root('tests', 'sdl_mouse.c')).read())) - - Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'sdl_mouse.c'), '-O2', '--minify', '0', '-o', 'page.html', '--pre-js', 'pre.js']).communicate() - self.run_browser('page.html', '', '/report_result?740') - - def test_sdl_mouse_offsets(self): - open(os.path.join(self.get_dir(), 'pre.js'), 'w').write(''' - function simulateMouseEvent(x, y, button) { - var event = document.createEvent("MouseEvents"); - if (button >= 0) { - var event1 = document.createEvent("MouseEvents"); - event1.initMouseEvent('mousedown', true, true, window, - 1, x, y, x, y, - 0, 0, 0, 0, - button, null); - Module['canvas'].dispatchEvent(event1); - var event2 = document.createEvent("MouseEvents"); - event2.initMouseEvent('mouseup', true, true, window, - 1, x, y, x, y, - 0, 0, 0, 0, - button, null); - Module['canvas'].dispatchEvent(event2); - } else { - var event1 = document.createEvent("MouseEvents"); - event1.initMouseEvent('mousemove', true, true, window, - 0, x, y, x, y, - 0, 0, 0, 0, - 0, null); - Module['canvas'].dispatchEvent(event1); - } - } - window['simulateMouseEvent'] = simulateMouseEvent; - ''') - open(os.path.join(self.get_dir(), 'page.html'), 'w').write(''' - - - - - -
- -
- - - - - - ''') - open(os.path.join(self.get_dir(), 'sdl_mouse.c'), 'w').write(self.with_report_result(open(path_from_root('tests', 'sdl_mouse.c')).read())) - - Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'sdl_mouse.c'), '-O2', '--minify', '0', '-o', 'sdl_mouse.js', '--pre-js', 'pre.js']).communicate() - self.run_browser('page.html', '', '/report_result?600') - - def test_sdl_audio(self): - shutil.copyfile(path_from_root('tests', 'sounds', 'alarmvictory_1.ogg'), os.path.join(self.get_dir(), 'sound.ogg')) - shutil.copyfile(path_from_root('tests', 'sounds', 'alarmcreatemiltaryfoot_1.wav'), os.path.join(self.get_dir(), 'sound2.wav')) - shutil.copyfile(path_from_root('tests', 'sounds', 'noise.ogg'), os.path.join(self.get_dir(), 'noise.ogg')) - shutil.copyfile(path_from_root('tests', 'sounds', 'the_entertainer.ogg'), os.path.join(self.get_dir(), 'the_entertainer.ogg')) - open(os.path.join(self.get_dir(), 'bad.ogg'), 'w').write('I claim to be audio, but am lying') - open(os.path.join(self.get_dir(), 'sdl_audio.c'), 'w').write(self.with_report_result(open(path_from_root('tests', 'sdl_audio.c')).read())) - - # use closure to check for a possible bug with closure minifying away newer Audio() attributes - Popen([PYTHON, EMCC, '-O2', '--closure', '1', '--minify', '0', os.path.join(self.get_dir(), 'sdl_audio.c'), '--preload-file', 'sound.ogg', '--preload-file', 'sound2.wav', '--embed-file', 'the_entertainer.ogg', '--preload-file', 'noise.ogg', '--preload-file', 'bad.ogg', '-o', 'page.html', '-s', 'EXPORTED_FUNCTIONS=["_main", "_play", "_play2"]']).communicate() - self.run_browser('page.html', '', '/report_result?1') - - def test_sdl_audio_mix_channels(self): - shutil.copyfile(path_from_root('tests', 'sounds', 'noise.ogg'), os.path.join(self.get_dir(), 'sound.ogg')) - open(os.path.join(self.get_dir(), 'sdl_audio_mix_channels.c'), 'w').write(self.with_report_result(open(path_from_root('tests', 'sdl_audio_mix_channels.c')).read())) - - Popen([PYTHON, EMCC, '-O2', '--minify', '0', os.path.join(self.get_dir(), 'sdl_audio_mix_channels.c'), '--preload-file', 'sound.ogg', '-o', 'page.html']).communicate() - self.run_browser('page.html', '', '/report_result?1') - - def test_sdl_audio_mix(self): - shutil.copyfile(path_from_root('tests', 'sounds', 'pluck.ogg'), os.path.join(self.get_dir(), 'sound.ogg')) - shutil.copyfile(path_from_root('tests', 'sounds', 'the_entertainer.ogg'), os.path.join(self.get_dir(), 'music.ogg')) - shutil.copyfile(path_from_root('tests', 'sounds', 'noise.ogg'), os.path.join(self.get_dir(), 'noise.ogg')) - open(os.path.join(self.get_dir(), 'sdl_audio_mix.c'), 'w').write(self.with_report_result(open(path_from_root('tests', 'sdl_audio_mix.c')).read())) - - Popen([PYTHON, EMCC, '-O2', '--minify', '0', os.path.join(self.get_dir(), 'sdl_audio_mix.c'), '--preload-file', 'sound.ogg', '--preload-file', 'music.ogg', '--preload-file', 'noise.ogg', '-o', 'page.html']).communicate() - self.run_browser('page.html', '', '/report_result?1') - - def test_sdl_audio_quickload(self): - open(os.path.join(self.get_dir(), 'sdl_audio_quickload.c'), 'w').write(self.with_report_result(open(path_from_root('tests', 'sdl_audio_quickload.c')).read())) - - Popen([PYTHON, EMCC, '-O2', '--minify', '0', os.path.join(self.get_dir(), 'sdl_audio_quickload.c'), '-o', 'page.html', '-s', 'EXPORTED_FUNCTIONS=["_main", "_play"]']).communicate() - self.run_browser('page.html', '', '/report_result?1') - - def test_sdl_gl_read(self): - # SDL, OpenGL, readPixels - open(os.path.join(self.get_dir(), 'sdl_gl_read.c'), 'w').write(self.with_report_result(open(path_from_root('tests', 'sdl_gl_read.c')).read())) - Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'sdl_gl_read.c'), '-o', 'something.html']).communicate() - self.run_browser('something.html', '.', '/report_result?1') - - def test_sdl_ogl(self): - shutil.copyfile(path_from_root('tests', 'screenshot.png'), os.path.join(self.get_dir(), 'screenshot.png')) - self.btest('sdl_ogl.c', reference='screenshot-gray-purple.png', reference_slack=1, - args=['-O2', '--minify', '0', '--preload-file', 'screenshot.png'], - message='You should see an image with gray at the top.') - - def test_sdl_ogl_defaultmatrixmode(self): - shutil.copyfile(path_from_root('tests', 'screenshot.png'), os.path.join(self.get_dir(), 'screenshot.png')) - self.btest('sdl_ogl_defaultMatrixMode.c', reference='screenshot-gray-purple.png', reference_slack=1, - args=['--minify', '0', '--preload-file', 'screenshot.png'], - message='You should see an image with gray at the top.') - - def test_sdl_ogl_p(self): - # Immediate mode with pointers - shutil.copyfile(path_from_root('tests', 'screenshot.png'), os.path.join(self.get_dir(), 'screenshot.png')) - self.btest('sdl_ogl_p.c', reference='screenshot-gray.png', reference_slack=1, - args=['--preload-file', 'screenshot.png'], - message='You should see an image with gray at the top.') - - def test_sdl_fog_simple(self): - shutil.copyfile(path_from_root('tests', 'screenshot.png'), os.path.join(self.get_dir(), 'screenshot.png')) - self.btest('sdl_fog_simple.c', reference='screenshot-fog-simple.png', - args=['-O2', '--minify', '0', '--preload-file', 'screenshot.png'], - message='You should see an image with fog.') - - def test_sdl_fog_negative(self): - shutil.copyfile(path_from_root('tests', 'screenshot.png'), os.path.join(self.get_dir(), 'screenshot.png')) - self.btest('sdl_fog_negative.c', reference='screenshot-fog-negative.png', - args=['--preload-file', 'screenshot.png'], - message='You should see an image with fog.') - - def test_sdl_fog_density(self): - shutil.copyfile(path_from_root('tests', 'screenshot.png'), os.path.join(self.get_dir(), 'screenshot.png')) - self.btest('sdl_fog_density.c', reference='screenshot-fog-density.png', - args=['--preload-file', 'screenshot.png'], - message='You should see an image with fog.') - - def test_sdl_fog_exp2(self): - shutil.copyfile(path_from_root('tests', 'screenshot.png'), os.path.join(self.get_dir(), 'screenshot.png')) - self.btest('sdl_fog_exp2.c', reference='screenshot-fog-exp2.png', - args=['--preload-file', 'screenshot.png'], - message='You should see an image with fog.') - - def test_sdl_fog_linear(self): - shutil.copyfile(path_from_root('tests', 'screenshot.png'), os.path.join(self.get_dir(), 'screenshot.png')) - self.btest('sdl_fog_linear.c', reference='screenshot-fog-linear.png', reference_slack=1, - args=['--preload-file', 'screenshot.png'], - message='You should see an image with fog.') - - def test_openal_playback(self): - shutil.copyfile(path_from_root('tests', 'sounds', 'audio.wav'), os.path.join(self.get_dir(), 'audio.wav')) - open(os.path.join(self.get_dir(), 'openal_playback.cpp'), 'w').write(self.with_report_result(open(path_from_root('tests', 'openal_playback.cpp')).read())) - - Popen([PYTHON, EMCC, '-O2', os.path.join(self.get_dir(), 'openal_playback.cpp'), '--preload-file', 'audio.wav', '-o', 'page.html']).communicate() - self.run_browser('page.html', '', '/report_result?1') - - def test_openal_buffers(self): - shutil.copyfile(path_from_root('tests', 'sounds', 'the_entertainer.wav'), os.path.join(self.get_dir(), 'the_entertainer.wav')) - self.btest('openal_buffers.c', '0', args=['--preload-file', 'the_entertainer.wav'],) - - def test_glfw(self): - open(os.path.join(self.get_dir(), 'glfw.c'), 'w').write(self.with_report_result(open(path_from_root('tests', 'glfw.c')).read())) - - Popen([PYTHON, EMCC, '-O2', os.path.join(self.get_dir(), 'glfw.c'), '-o', 'page.html']).communicate() - self.run_browser('page.html', '', '/report_result?1') - - def test_egl_width_height(self): - open(os.path.join(self.get_dir(), 'test_egl_width_height.c'), 'w').write(self.with_report_result(open(path_from_root('tests', 'test_egl_width_height.c')).read())) - - Popen([PYTHON, EMCC, '-O2', os.path.join(self.get_dir(), 'test_egl_width_height.c'), '-o', 'page.html']).communicate() - self.run_browser('page.html', 'Should print "(300, 150)" -- the size of the canvas in pixels', '/report_result?1') - - def test_freealut(self): - programs = self.get_library('freealut', os.path.join('examples', '.libs', 'hello_world.bc'), make_args=['EXEEXT=.bc']) - for program in programs: - assert os.path.exists(program) - Popen([PYTHON, EMCC, '-O2', program, '-o', 'page.html']).communicate() - self.run_browser('page.html', 'You should hear "Hello World!"') - - def test_worker(self): - # Test running in a web worker - output = Popen([PYTHON, EMCC, path_from_root('tests', 'hello_world_worker.cpp'), '-o', 'worker.js'], stdout=PIPE, stderr=PIPE).communicate() - assert len(output[0]) == 0, output[0] - assert os.path.exists('worker.js'), output - self.assertContained('you should not see this text when in a worker!', run_js('worker.js')) # code should run standalone - html_file = open('main.html', 'w') - html_file.write(''' - - - Worker Test - - - - ''') - html_file.close() - self.run_browser('main.html', 'You should see that the worker was called, and said "hello from worker!"', '/report_result?hello%20from%20worker!') - - def test_chunked_synchronous_xhr(self): - main = 'chunked_sync_xhr.html' - worker_filename = "download_and_checksum_worker.js" - - html_file = open(main, 'w') - html_file.write(r""" - - - Chunked XHR - - - Chunked XHR Web Worker Test - - - - """) - html_file.close() - - c_source_filename = "checksummer.c" - - prejs_filename = "worker_prejs.js" - prejs_file = open(prejs_filename, 'w') - prejs_file.write(r""" - if (typeof(Module) === "undefined") Module = {}; - Module["arguments"] = ["/bigfile"]; - Module["preInit"] = function() { - FS.createLazyFile('/', "bigfile", "http://localhost:11111/bogus_file_path", true, false); - }; - var doTrace = true; - Module["print"] = function(s) { self.postMessage({channel: "stdout", line: s}); }; - Module["stderr"] = function(s) { self.postMessage({channel: "stderr", char: s, trace: ((doTrace && s === 10) ? new Error().stack : null)}); doTrace = false; }; - """) - prejs_file.close() - # vs. os.path.join(self.get_dir(), filename) - # vs. path_from_root('tests', 'hello_world_gles.c') - Popen([PYTHON, EMCC, path_from_root('tests', c_source_filename), '-g', '-s', 'SMALL_CHUNKS=1', '-o', worker_filename, - '--pre-js', prejs_filename]).communicate() - - chunkSize = 1024 - data = os.urandom(10*chunkSize+1) # 10 full chunks and one 1 byte chunk - expectedConns = 11 - import zlib - checksum = zlib.adler32(data) - - def chunked_server(support_byte_ranges): - class ChunkedServerHandler(BaseHTTPServer.BaseHTTPRequestHandler): - def sendheaders(s, extra=[], length=len(data)): - s.send_response(200) - s.send_header("Content-Length", str(length)) - s.send_header("Access-Control-Allow-Origin", "http://localhost:8888") - s.send_header("Access-Control-Expose-Headers", "Content-Length, Accept-Ranges") - s.send_header("Content-type", "application/octet-stream") - if support_byte_ranges: - s.send_header("Accept-Ranges", "bytes") - for i in extra: - s.send_header(i[0], i[1]) - s.end_headers() - - def do_HEAD(s): - s.sendheaders() - - def do_OPTIONS(s): - s.sendheaders([("Access-Control-Allow-Headers", "Range")], 0) - - def do_GET(s): - if not support_byte_ranges: - s.sendheaders() - s.wfile.write(data) - else: - (start, end) = s.headers.get("range").split("=")[1].split("-") - start = int(start) - end = int(end) - end = min(len(data)-1, end) - length = end-start+1 - s.sendheaders([],length) - s.wfile.write(data[start:end+1]) - s.wfile.close() - httpd = BaseHTTPServer.HTTPServer(('localhost', 11111), ChunkedServerHandler) - for i in range(expectedConns+1): - httpd.handle_request() - - server = multiprocessing.Process(target=chunked_server, args=(True,)) - server.start() - self.run_browser(main, 'Chunked binary synchronous XHR in Web Workers!', '/report_result?' + str(checksum)) - server.terminate() - - def test_glgears(self): - self.btest('hello_world_gles.c', reference='gears.png', - args=['-DHAVE_BUILTIN_SINCOS'], outfile='something.html', - message='You should see animating gears.') - - def test_glgears_animation(self): - es2_suffix = ['', '_full', '_full_944'] - for full_es2 in [0, 1, 2]: - for emulation in [0, 1]: - if full_es2 and emulation: continue - print full_es2, emulation - Popen([PYTHON, EMCC, path_from_root('tests', 'hello_world_gles%s.c' % es2_suffix[full_es2]), '-o', 'something.html', - '-DHAVE_BUILTIN_SINCOS', '-s', 'GL_TESTING=1', - '--shell-file', path_from_root('tests', 'hello_world_gles_shell.html')] + - (['-s', 'FORCE_GL_EMULATION=1'] if emulation else []) + - (['-s', 'FULL_ES2=1'] if full_es2 else []), - ).communicate() - self.run_browser('something.html', 'You should see animating gears.', '/report_gl_result?true') - assert ('var GLEmulation' in open(self.in_dir('something.html')).read()) == emulation, "emulation code should be added when asked for" - - def test_fulles2_sdlproc(self): - self.btest('full_es2_sdlproc.c', '1', args=['-s', 'GL_TESTING=1', '-DHAVE_BUILTIN_SINCOS', '-s', 'FULL_ES2=1']) - - def test_glgears_deriv(self): - self.btest('hello_world_gles_deriv.c', reference='gears.png', - args=['-DHAVE_BUILTIN_SINCOS'], outfile='something.html', - message='You should see animating gears.') - with open('something.html') as f: - assert 'gl-matrix' not in f.read(), 'Should not include glMatrix when not needed' - - def test_glbook(self): - programs = self.get_library('glbook', [ - os.path.join('Chapter_2', 'Hello_Triangle', 'CH02_HelloTriangle.bc'), - os.path.join('Chapter_8', 'Simple_VertexShader', 'CH08_SimpleVertexShader.bc'), - os.path.join('Chapter_9', 'Simple_Texture2D', 'CH09_SimpleTexture2D.bc'), - os.path.join('Chapter_9', 'Simple_TextureCubemap', 'CH09_TextureCubemap.bc'), - os.path.join('Chapter_9', 'TextureWrap', 'CH09_TextureWrap.bc'), - os.path.join('Chapter_10', 'MultiTexture', 'CH10_MultiTexture.bc'), - os.path.join('Chapter_13', 'ParticleSystem', 'CH13_ParticleSystem.bc'), - ], configure=None) - def book_path(*pathelems): - return path_from_root('tests', 'glbook', *pathelems) - for program in programs: - print program - basename = os.path.basename(program) - args = [] - if basename == 'CH10_MultiTexture.bc': - shutil.copyfile(book_path('Chapter_10', 'MultiTexture', 'basemap.tga'), os.path.join(self.get_dir(), 'basemap.tga')) - shutil.copyfile(book_path('Chapter_10', 'MultiTexture', 'lightmap.tga'), os.path.join(self.get_dir(), 'lightmap.tga')) - args = ['--preload-file', 'basemap.tga', '--preload-file', 'lightmap.tga'] - elif basename == 'CH13_ParticleSystem.bc': - shutil.copyfile(book_path('Chapter_13', 'ParticleSystem', 'smoke.tga'), os.path.join(self.get_dir(), 'smoke.tga')) - args = ['--preload-file', 'smoke.tga', '-O2'] # test optimizations and closure here as well for more coverage - - self.btest(program, - reference=book_path(basename.replace('.bc', '.png')), args=args) - - def btest(self, filename, expected=None, reference=None, force_c=False, reference_slack=0, - args=[], outfile='test.html', message='.'): # TODO: use in all other tests - # if we are provided the source and not a path, use that - filename_is_src = '\n' in filename - src = filename if filename_is_src else '' - filepath = path_from_root('tests', filename) if not filename_is_src else ('main.c' if force_c else 'main.cpp') - temp_filepath = os.path.join(self.get_dir(), os.path.basename(filepath)) - if filename_is_src: - with open(temp_filepath, 'w') as f: f.write(src) - if not reference: - if not src: - with open(filepath) as f: src = f.read() - with open(temp_filepath, 'w') as f: f.write(self.with_report_result(src)) - else: - expected = [str(i) for i in range(0, reference_slack+1)] - shutil.copyfile(filepath, temp_filepath) - self.reftest(path_from_root('tests', reference)) - args = args + ['--pre-js', 'reftest.js', '-s', 'GL_TESTING=1'] - Popen([PYTHON, EMCC, temp_filepath, '-o', outfile] + args).communicate() - if type(expected) is str: expected = [expected] - self.run_browser(outfile, message, ['/report_result?' + e for e in expected]) - - def test_gles2_emulation(self): - shutil.copyfile(path_from_root('tests', 'glbook', 'Chapter_10', 'MultiTexture', 'basemap.tga'), self.in_dir('basemap.tga')) - shutil.copyfile(path_from_root('tests', 'glbook', 'Chapter_10', 'MultiTexture', 'lightmap.tga'), self.in_dir('lightmap.tga')) - shutil.copyfile(path_from_root('tests', 'glbook', 'Chapter_13', 'ParticleSystem', 'smoke.tga'), self.in_dir('smoke.tga')) - - for source, reference in [ - (os.path.join('glbook', 'Chapter_2', 'Hello_Triangle', 'Hello_Triangle_orig.c'), path_from_root('tests', 'glbook', 'CH02_HelloTriangle.png')), - #(os.path.join('glbook', 'Chapter_8', 'Simple_VertexShader', 'Simple_VertexShader_orig.c'), path_from_root('tests', 'glbook', 'CH08_SimpleVertexShader.png')), # XXX needs INT extension in WebGL - (os.path.join('glbook', 'Chapter_9', 'TextureWrap', 'TextureWrap_orig.c'), path_from_root('tests', 'glbook', 'CH09_TextureWrap.png')), - #(os.path.join('glbook', 'Chapter_9', 'Simple_TextureCubemap', 'Simple_TextureCubemap_orig.c'), path_from_root('tests', 'glbook', 'CH09_TextureCubemap.png')), # XXX needs INT extension in WebGL - (os.path.join('glbook', 'Chapter_9', 'Simple_Texture2D', 'Simple_Texture2D_orig.c'), path_from_root('tests', 'glbook', 'CH09_SimpleTexture2D.png')), - (os.path.join('glbook', 'Chapter_10', 'MultiTexture', 'MultiTexture_orig.c'), path_from_root('tests', 'glbook', 'CH10_MultiTexture.png')), - (os.path.join('glbook', 'Chapter_13', 'ParticleSystem', 'ParticleSystem_orig.c'), path_from_root('tests', 'glbook', 'CH13_ParticleSystem.png')), - ]: - print source - self.btest(source, - reference=reference, - args=['-I' + path_from_root('tests', 'glbook', 'Common'), - path_from_root('tests', 'glbook', 'Common', 'esUtil.c'), - path_from_root('tests', 'glbook', 'Common', 'esShader.c'), - path_from_root('tests', 'glbook', 'Common', 'esShapes.c'), - path_from_root('tests', 'glbook', 'Common', 'esTransform.c'), - '-s', 'FULL_ES2=1', - '--preload-file', 'basemap.tga', '--preload-file', 'lightmap.tga', '--preload-file', 'smoke.tga']) - - def test_emscripten_api(self): - self.btest('emscripten_api_browser.cpp', '1', args=['-s', '''EXPORTED_FUNCTIONS=['_main', '_third']''']) - - def test_emscripten_api_infloop(self): - self.btest('emscripten_api_browser_infloop.cpp', '7') - - def test_emscripten_fs_api(self): - shutil.copyfile(path_from_root('tests', 'screenshot.png'), os.path.join(self.get_dir(), 'screenshot.png')) # preloaded *after* run - self.btest('emscripten_fs_api_browser.cpp', '1') - - def test_sdl_quit(self): - self.btest('sdl_quit.c', '1') - - def test_sdl_resize(self): - self.btest('sdl_resize.c', '1') - - def test_gc(self): - self.btest('browser_gc.cpp', '1') - - def test_glshaderinfo(self): - self.btest('glshaderinfo.cpp', '1') - - def test_glgetattachedshaders(self): - self.btest('glgetattachedshaders.c', '1') - - def test_sdlglshader(self): - self.btest('sdlglshader.c', reference='sdlglshader.png', args=['-O2', '--closure', '1']) - - def test_gl_ps(self): - # pointers and a shader - shutil.copyfile(path_from_root('tests', 'screenshot.png'), os.path.join(self.get_dir(), 'screenshot.png')) - self.btest('gl_ps.c', reference='gl_ps.png', args=['--preload-file', 'screenshot.png'], reference_slack=1) - - def test_gl_ps_packed(self): - # packed data that needs to be strided - shutil.copyfile(path_from_root('tests', 'screenshot.png'), os.path.join(self.get_dir(), 'screenshot.png')) - self.btest('gl_ps_packed.c', reference='gl_ps.png', args=['--preload-file', 'screenshot.png'], reference_slack=1) - - def test_gl_ps_strides(self): - shutil.copyfile(path_from_root('tests', 'screenshot.png'), os.path.join(self.get_dir(), 'screenshot.png')) - self.btest('gl_ps_strides.c', reference='gl_ps_strides.png', args=['--preload-file', 'screenshot.png']) - - def test_gl_renderers(self): - self.btest('gl_renderers.c', reference='gl_renderers.png', args=['-s', 'GL_UNSAFE_OPTS=0']) - - def test_gl_stride(self): - self.btest('gl_stride.c', reference='gl_stride.png', args=['-s', 'GL_UNSAFE_OPTS=0']) - - def test_matrix_identity(self): - self.btest('gl_matrix_identity.c', expected=['-1882984448', '460451840']) - - def test_cubegeom_pre(self): - self.btest('cubegeom_pre.c', reference='cubegeom_pre.png') - - def test_cubegeom_pre2(self): - self.btest('cubegeom_pre2.c', reference='cubegeom_pre2.png', args=['-s', 'GL_DEBUG=1']) # some coverage for GL_DEBUG not breaking the build - - def test_cubegeom_pre3(self): - self.btest('cubegeom_pre3.c', reference='cubegeom_pre2.png') - - def test_cubegeom(self): - self.btest('cubegeom.c', args=['-O2', '-g'], reference='cubegeom.png') - - def test_cubegeom_glew(self): - self.btest('cubegeom_glew.c', args=['-O2', '--closure', '1'], reference='cubegeom.png') - - def test_cubegeom_color(self): - self.btest('cubegeom_color.c', reference='cubegeom_color.png') - - def test_cubegeom_normal(self): - self.btest('cubegeom_normal.c', reference='cubegeom_normal.png') - - def test_cubegeom_normal_dap(self): # draw is given a direct pointer to clientside memory, no element array buffer - self.btest('cubegeom_normal_dap.c', reference='cubegeom_normal.png') - - def test_cubegeom_normal_dap_far(self): # indices do nto start from 0 - self.btest('cubegeom_normal_dap_far.c', reference='cubegeom_normal.png') - - def test_cubegeom_normal_dap_far_range(self): # glDrawRangeElements - self.btest('cubegeom_normal_dap_far_range.c', reference='cubegeom_normal.png') - - def test_cubegeom_normal_dap_far_glda(self): # use glDrawArrays - self.btest('cubegeom_normal_dap_far_glda.c', reference='cubegeom_normal_dap_far_glda.png') - - def test_cubegeom_normal_dap_far_glda_quad(self): # with quad - self.btest('cubegeom_normal_dap_far_glda_quad.c', reference='cubegeom_normal_dap_far_glda_quad.png') - - def test_cubegeom_mt(self): - self.btest('cubegeom_mt.c', reference='cubegeom_mt.png') # multitexture - - def test_cubegeom_color2(self): - self.btest('cubegeom_color2.c', reference='cubegeom_color2.png') - - def test_cubegeom_texturematrix(self): - self.btest('cubegeom_texturematrix.c', reference='cubegeom_texturematrix.png') - - def test_cubegeom_fog(self): - self.btest('cubegeom_fog.c', reference='cubegeom_fog.png') - - def test_cubegeom_pre_vao(self): - self.btest('cubegeom_pre_vao.c', reference='cubegeom_pre_vao.png') - - def test_cubegeom_pre2_vao(self): - self.btest('cubegeom_pre2_vao.c', reference='cubegeom_pre_vao.png') - - def test_cubegeom_pre2_vao2(self): - self.btest('cubegeom_pre2_vao2.c', reference='cubegeom_pre2_vao2.png') - - def test_cube_explosion(self): - self.btest('cube_explosion.c', reference='cube_explosion.png') - - def test_sdl_canvas_blank(self): - self.btest('sdl_canvas_blank.c', reference='sdl_canvas_blank.png') - - def test_sdl_canvas_palette(self): - self.btest('sdl_canvas_palette.c', reference='sdl_canvas_palette.png') - - def test_sdl_canvas_twice(self): - self.btest('sdl_canvas_twice.c', reference='sdl_canvas_twice.png') - - def test_sdl_maprgba(self): - self.btest('sdl_maprgba.c', reference='sdl_maprgba.png', reference_slack=3) - - def test_sdl_rotozoom(self): - shutil.copyfile(path_from_root('tests', 'screenshot.png'), os.path.join(self.get_dir(), 'screenshot.png')) - self.btest('sdl_rotozoom.c', reference='sdl_rotozoom.png', args=['--preload-file', 'screenshot.png'], reference_slack=5) - - def test_sdl_gfx_primitives(self): - self.btest('sdl_gfx_primitives.c', reference='sdl_gfx_primitives.png', reference_slack=1) - - def test_sdl_canvas_palette_2(self): - open(os.path.join(self.get_dir(), 'pre.js'), 'w').write(''' - Module['preRun'].push(function() { - SDL.defaults.copyOnLock = false; - }); - ''') - - open(os.path.join(self.get_dir(), 'args-r.js'), 'w').write(''' - Module['arguments'] = ['-r']; - ''') - - open(os.path.join(self.get_dir(), 'args-g.js'), 'w').write(''' - Module['arguments'] = ['-g']; - ''') - - open(os.path.join(self.get_dir(), 'args-b.js'), 'w').write(''' - Module['arguments'] = ['-b']; - ''') - - self.btest('sdl_canvas_palette_2.c', reference='sdl_canvas_palette_r.png', args=['--pre-js', 'pre.js', '--pre-js', 'args-r.js']) - self.btest('sdl_canvas_palette_2.c', reference='sdl_canvas_palette_g.png', args=['--pre-js', 'pre.js', '--pre-js', 'args-g.js']) - self.btest('sdl_canvas_palette_2.c', reference='sdl_canvas_palette_b.png', args=['--pre-js', 'pre.js', '--pre-js', 'args-b.js']) - - def test_sdl_alloctext(self): - self.btest('sdl_alloctext.c', expected='1', args=['-O2', '-s', 'TOTAL_MEMORY=' + str(1024*1024*8)]) - - def test_sdl_surface_refcount(self): - self.btest('sdl_surface_refcount.c', expected='1') - - def test_glbegin_points(self): - shutil.copyfile(path_from_root('tests', 'screenshot.png'), os.path.join(self.get_dir(), 'screenshot.png')) - self.btest('glbegin_points.c', reference='glbegin_points.png', args=['--preload-file', 'screenshot.png']) - - def test_s3tc(self): - shutil.copyfile(path_from_root('tests', 'screenshot.dds'), os.path.join(self.get_dir(), 'screenshot.dds')) - self.btest('s3tc.c', reference='s3tc.png', args=['--preload-file', 'screenshot.dds']) - - def test_s3tc_crunch(self): - shutil.copyfile(path_from_root('tests', 'ship.dds'), 'ship.dds') - shutil.copyfile(path_from_root('tests', 'bloom.dds'), 'bloom.dds') - shutil.copyfile(path_from_root('tests', 'water.dds'), 'water.dds') - Popen([PYTHON, FILE_PACKAGER, 'test.data', '--pre-run', '--crunch', '--preload', 'ship.dds', 'bloom.dds', 'water.dds'], stdout=open('pre.js', 'w')).communicate() - assert os.stat('test.data').st_size < 0.5*(os.stat('ship.dds').st_size+os.stat('bloom.dds').st_size+os.stat('water.dds').st_size), 'Compressed should be smaller than dds' - shutil.move('ship.dds', 'ship.donotfindme.dds') # make sure we load from the compressed - shutil.move('bloom.dds', 'bloom.donotfindme.dds') # make sure we load from the compressed - shutil.move('water.dds', 'water.donotfindme.dds') # make sure we load from the compressed - self.btest('s3tc_crunch.c', reference='s3tc_crunch.png', reference_slack=11, args=['--pre-js', 'pre.js']) - - def test_s3tc_crunch_split(self): # load several datafiles/outputs of file packager - shutil.copyfile(path_from_root('tests', 'ship.dds'), 'ship.dds') - shutil.copyfile(path_from_root('tests', 'bloom.dds'), 'bloom.dds') - shutil.copyfile(path_from_root('tests', 'water.dds'), 'water.dds') - Popen([PYTHON, FILE_PACKAGER, 'asset_a.data', '--pre-run', '--crunch', '--preload', 'ship.dds', 'bloom.dds'], stdout=open('asset_a.js', 'w')).communicate() - Popen([PYTHON, FILE_PACKAGER, 'asset_b.data', '--pre-run', '--crunch', '--preload', 'water.dds'], stdout=open('asset_b.js', 'w')).communicate() - shutil.move('ship.dds', 'ship.donotfindme.dds') # make sure we load from the compressed - shutil.move('bloom.dds', 'bloom.donotfindme.dds') # make sure we load from the compressed - shutil.move('water.dds', 'water.donotfindme.dds') # make sure we load from the compressed - self.btest('s3tc_crunch.c', reference='s3tc_crunch.png', reference_slack=11, args=['--pre-js', 'asset_a.js', '--pre-js', 'asset_b.js']) - - def test_aniso(self): - shutil.copyfile(path_from_root('tests', 'water.dds'), 'water.dds') - self.btest('aniso.c', reference='aniso.png', reference_slack=2, args=['--preload-file', 'water.dds']) - - def test_tex_nonbyte(self): - self.btest('tex_nonbyte.c', reference='tex_nonbyte.png') - - def test_float_tex(self): - self.btest('float_tex.cpp', reference='float_tex.png') - - def test_subdata(self): - self.btest('gl_subdata.cpp', reference='float_tex.png') - - def test_perspective(self): - self.btest('perspective.c', reference='perspective.png') - - def test_runtimelink(self): - return self.skip('shared libs are deprecated') - main, supp = self.setup_runtimelink_test() - - open(self.in_dir('supp.cpp'), 'w').write(supp) - Popen([PYTHON, EMCC, self.in_dir('supp.cpp'), '-o', 'supp.js', '-s', 'LINKABLE=1', '-s', 'NAMED_GLOBALS=1', '-s', 'BUILD_AS_SHARED_LIB=2', '-O2', '-s', 'ASM_JS=0']).communicate() - shutil.move(self.in_dir('supp.js'), self.in_dir('supp.so')) - - self.btest(main, args=['-s', 'LINKABLE=1', '-s', 'NAMED_GLOBALS=1', '-s', 'RUNTIME_LINKED_LIBS=["supp.so"]', '-DBROWSER=1', '-O2', '-s', 'ASM_JS=0'], expected='76') - - def test_pre_run_deps(self): - # Adding a dependency in preRun will delay run - open(os.path.join(self.get_dir(), 'pre.js'), 'w').write(''' - Module.preRun = function() { - addRunDependency(); - Module.print('preRun called, added a dependency...'); - setTimeout(function() { - Module.okk = 10; - removeRunDependency() - }, 2000); - }; - ''') - self.btest('pre_run_deps.cpp', expected='10', args=['--pre-js', 'pre.js']) - - def test_worker_api(self): - Popen([PYTHON, EMCC, path_from_root('tests', 'worker_api_worker.cpp'), '-o', 'worker.js', '-s', 'BUILD_AS_WORKER=1', '-s', 'EXPORTED_FUNCTIONS=["_one"]']).communicate() - self.btest('worker_api_main.cpp', expected='566') - - def test_worker_api_2(self): - Popen([PYTHON, EMCC, path_from_root('tests', 'worker_api_2_worker.cpp'), '-o', 'worker.js', '-s', 'BUILD_AS_WORKER=1', '-O2', '--minify', '0', '-s', 'EXPORTED_FUNCTIONS=["_one", "_two", "_three", "_four"]']).communicate() - self.btest('worker_api_2_main.cpp', args=['-O2', '--minify', '0'], expected='11') - - def test_emscripten_async_wget2(self): - self.btest('http.cpp', expected='0', args=['-I' + path_from_root('tests')]) - - @staticmethod - def clean_pids(pids): - import signal, errno - def pid_exists(pid): - try: - # NOTE: may just kill the process in Windows - os.kill(pid, 0) - except OSError, e: - return e.errno == errno.EPERM - else: - return True - def kill_pids(pids, sig): - for pid in pids: - if not pid_exists(pid): - break - print '[killing %d]' % pid - try: - os.kill(pid, sig) - print '[kill succeeded]' - except: - print '[kill fail]' - # ask nicely (to try and catch the children) - kill_pids(pids, signal.SIGTERM) - time.sleep(1) - # extreme prejudice, may leave children - kill_pids(pids, signal.SIGKILL) - - class WebsockifyServerHarness: - def __init__(self, filename, args, listen_port, target_port): - self.pids = [] - self.filename = filename - self.target_port = target_port - self.listen_port = listen_port - self.args = args or [] - - def __enter__(self): - import socket, websockify - - # compile the server - # NOTE empty filename support is a hack to support - # the current test_enet - if self.filename: - Popen([CLANG_CC, path_from_root('tests', self.filename), '-o', 'server'] + self.args).communicate() - process = Popen([os.path.abspath('server')]) - self.pids.append(process.pid) - - # start the websocket proxy - print >> sys.stderr, 'running websockify on %d, forward to tcp %d' % (self.listen_port, self.target_port) - wsp = websockify.WebSocketProxy(verbose=True, listen_port=self.listen_port, target_host="127.0.0.1", target_port=self.target_port, run_once=True) - self.websockify = multiprocessing.Process(target=wsp.start_server) - self.websockify.start() - self.pids.append(self.websockify.pid) - print '[Websockify on process %s]' % str(self.pids[-2:]) - - def __exit__(self, *args, **kwargs): - # try to kill the websockify proxy gracefully - if self.websockify.is_alive(): - self.websockify.terminate() - self.websockify.join() - - # clean up any processes we started - browser.clean_pids(self.pids) - - - class CompiledServerHarness: - def __init__(self, filename, args): - self.pids = browser.PidContainer() - self.filename = filename - self.args = args or [] - - def __enter__(self): - import socket, websockify - - # compile the server - Popen([PYTHON, EMCC, path_from_root('tests', self.filename), '-o', 'server.js'] + self.args).communicate() - process = Popen([NODE_JS, 'server.js']) - self.pids.append(process.pid) - - def __exit__(self, *args, **kwargs): - # clean up any processes we started - self.pids.kill_all() - - def make_relay_server(self, port1, port2): - print >> sys.stderr, 'creating relay server on ports %d,%d' % (port1, port2) - proc = Popen([PYTHON, path_from_root('tests', 'sockets', 'socket_relay.py'), str(port1), str(port2)]) - return proc - - # always run these tests last - # make sure to use different ports in each one because it takes a while for the processes to be cleaned up - - # NOTE all datagram tests are temporarily disabled, as - # we can't truly test datagram sockets until we have - # proper listen server support. - - def test_sockets_echo(self): - sockets_include = '-I'+path_from_root('tests', 'sockets') - - for datagram in [0]: - dgram_define = '-DTEST_DGRAM=%d' % datagram - - for harness in [ - self.WebsockifyServerHarness(os.path.join('sockets', 'test_sockets_echo_server.c'), ['-DSOCKK=8990', dgram_define, sockets_include], 8991, 8990) - # self.CompiledServerHarness(os.path.join('sockets', 'test_sockets_echo_server.c'), ['-DSOCKK=8990', dgram_define, sockets_include]) - ]: - with harness: - self.btest(os.path.join('sockets', 'test_sockets_echo_client.c'), expected='0', args=['-DSOCKK=8991', dgram_define, sockets_include]) - - def test_sockets_echo_bigdata(self): - sockets_include = '-I'+path_from_root('tests', 'sockets') - - for datagram in [0]: - dgram_define = '-DTEST_DGRAM=%d' % datagram - - # generate a large string literal to use as our message - message = '' - for i in range(256*256*2): - message += str(unichr(ord('a') + (i % 26))) - - # re-write the client test with this literal (it's too big to pass via command line) - input_filename = path_from_root('tests', 'sockets', 'test_sockets_echo_client.c') - input = open(input_filename).read() - output = input.replace('#define MESSAGE "pingtothepong"', '#define MESSAGE "%s"' % message) - - for harness in [ - self.WebsockifyServerHarness(os.path.join('sockets', 'test_sockets_echo_server.c'), ['-DSOCKK=8992', dgram_define, sockets_include], 8993, 8992) - ]: - with harness: - self.btest(output, expected='0', args=['-DSOCKK=8993', dgram_define, sockets_include], force_c=True) - - def test_sockets_partial(self): - for harness in [ - self.WebsockifyServerHarness(os.path.join('sockets', 'test_sockets_partial_server.c'), ['-DSOCKK=8994'], 8995, 8994) - ]: - with harness: - self.btest(os.path.join('sockets', 'test_sockets_partial_client.c'), expected='165', args=['-DSOCKK=8995']) - - # TODO add support for gethostbyaddr to re-enable this test - # def test_sockets_gethostbyname(self): - # self.btest(os.path.join('sockets', 'test_sockets_gethostbyname.c'), expected='0', args=['-O2', '-DSOCKK=8997']) - - def test_sockets_select_server_no_accept(self): - for harness in [ - self.WebsockifyServerHarness(os.path.join('sockets', 'test_sockets_select_server_no_accept_server.c'), ['-DSOCKK=8995'], 8996, 8995) - ]: - self.btest(os.path.join('sockets', 'test_sockets_select_server_no_accept_client.c'), expected='266', args=['-DSOCKK=8996']) - - def test_sockets_select_server_closes_connection_rw(self): - sockets_include = '-I'+path_from_root('tests', 'sockets') - - for harness in [ - self.WebsockifyServerHarness(os.path.join('sockets', 'test_sockets_echo_server.c'), ['-DSOCKK=9004', sockets_include], 9005, 9004) - ]: - with harness: - self.btest(os.path.join('sockets', 'test_sockets_select_server_closes_connection_client_rw.c'), expected='266', args=['-DSOCKK=9005', sockets_include]) - - # TODO remove this once we have proper listen server support built into emscripten. - # being that enet uses datagram sockets, we can't proxy to a native server with - # websockify, so we're emulating the listen server in the browser and relaying - # between two TCP servers. - def test_enet(self): - try_delete(self.in_dir('enet')) - shutil.copytree(path_from_root('tests', 'enet'), self.in_dir('enet')) - pwd = os.getcwd() - os.chdir(self.in_dir('enet')) - Popen([PYTHON, path_from_root('emconfigure'), './configure']).communicate() - Popen([PYTHON, path_from_root('emmake'), 'make']).communicate() - enet = [self.in_dir('enet', '.libs', 'libenet.a'), '-I'+path_from_root('tests', 'enet', 'include')] - os.chdir(pwd) - Popen([PYTHON, EMCC, path_from_root('tests', 'sockets', 'test_enet_server.c'), '-o', 'server.html', '-DSOCKK=2235'] + enet).communicate() - - with self.WebsockifyServerHarness('', [], 2235, 2234): - with self.WebsockifyServerHarness('', [], 2237, 2236): - pids = [] - try: - proc = self.make_relay_server(2234, 2236) - pids.append(proc.pid) - self.btest(os.path.join('sockets', 'test_enet_client.c'), expected='0', args=['-DSOCKK=2237'] + enet) - finally: - browser.clean_pids(pids); - - # TODO use this once we have listen server support - # def test_enet(self): - # try_delete(self.in_dir('enet')) - # shutil.copytree(path_from_root('tests', 'enet'), self.in_dir('enet')) - # pwd = os.getcwd() - # os.chdir(self.in_dir('enet')) - # Popen([PYTHON, path_from_root('emconfigure'), './configure']).communicate() - # Popen([PYTHON, path_from_root('emmake'), 'make']).communicate() - # enet = [self.in_dir('enet', '.libs', 'libenet.a'), '-I'+path_from_root('tests', 'enet', 'include')] - # os.chdir(pwd) - - # for harness in [ - # self.CompiledServerHarness(os.path.join('sockets', 'test_enet_server.c'), ['-DSOCKK=9010'] + enet, 9011, 9010) - # ]: - # with harness: - # self.btest(os.path.join('sockets', 'test_enet_client.c'), expected='0', args=['-DSOCKK=9011'] + enet) - -elif 'benchmark' in str(sys.argv): - # Benchmarks. Run them with argument |benchmark|. To run a specific test, do - # |benchmark.test_X|. - - fingerprint = [time.asctime()] - try: - fingerprint.append('em: ' + Popen(['git', 'show'], stdout=PIPE).communicate()[0].split('\n')[0]) - except: - pass - try: - d = os.getcwd() - os.chdir(os.path.expanduser('~/Dev/mozilla-central')) - fingerprint.append('sm: ' + filter(lambda line: 'changeset' in line, - Popen(['hg', 'tip'], stdout=PIPE).communicate()[0].split('\n'))[0]) - except: - pass - finally: - os.chdir(d) - fingerprint.append('llvm: ' + LLVM_ROOT) - print 'Running Emscripten benchmarks... [ %s ]' % ' | '.join(fingerprint) - - sys.argv = filter(lambda x: x != 'benchmark', sys.argv) - - assert(os.path.exists(CLOSURE_COMPILER)) - - try: - index = SPIDERMONKEY_ENGINE.index("options('strict')") - SPIDERMONKEY_ENGINE = SPIDERMONKEY_ENGINE[:index-1] + SPIDERMONKEY_ENGINE[index+1:] # closure generates non-strict - except: - pass - - Building.COMPILER = CLANG - - # Pick the JS engine to benchmark. If you specify one, it will be picked. For example, python tests/runner.py benchmark SPIDERMONKEY_ENGINE - JS_ENGINE = JS_ENGINES[0] - for i in range(1, len(sys.argv)): - arg = sys.argv[i] - if not arg.startswith('benchmark.test_'): - JS_ENGINE = eval(arg) - sys.argv[i] = None - sys.argv = filter(lambda arg: arg is not None, sys.argv) - print 'Benchmarking JS engine:', ' '.join(JS_ENGINE) - - Building.COMPILER_TEST_OPTS = [] - - TEST_REPS = 2 - TOTAL_TESTS = 8 - - # standard arguments for timing: - # 0: no runtime, just startup - # 1: very little runtime - # 2: 0.5 seconds - # 3: 1 second - # 4: 5 seconds - # 5: 10 seconds - DEFAULT_ARG = '4' - - tests_done = 0 - total_times = map(lambda x: 0., range(TOTAL_TESTS)) - total_native_times = map(lambda x: 0., range(TOTAL_TESTS)) - - class benchmark(RunnerCore): - save_dir = True - - def print_stats(self, times, native_times, last=False, reps=TEST_REPS): - if reps == 0: - print '(no reps)' - return - mean = sum(times)/len(times) - squared_times = map(lambda x: x*x, times) - mean_of_squared = sum(squared_times)/len(times) - std = math.sqrt(mean_of_squared - mean*mean) - sorted_times = times[:] - sorted_times.sort() - median = sum(sorted_times[len(sorted_times)/2 - 1:len(sorted_times)/2 + 1])/2 - - mean_native = sum(native_times)/len(native_times) - squared_native_times = map(lambda x: x*x, native_times) - mean_of_squared_native = sum(squared_native_times)/len(native_times) - std_native = math.sqrt(mean_of_squared_native - mean_native*mean_native) - sorted_native_times = native_times[:] - sorted_native_times.sort() - median_native = sum(sorted_native_times[len(sorted_native_times)/2 - 1:len(sorted_native_times)/2 + 1])/2 - - final = mean / mean_native - - if last: - norm = 0 - for i in range(len(times)): - norm += times[i]/native_times[i] - norm /= len(times) - print - print ' JavaScript: %.3f Native: %.3f Ratio: %.3f Normalized ratio: %.3f' % (mean, mean_native, final, norm) - return - - print - print ' JavaScript: mean: %.3f (+-%.3f) secs median: %.3f range: %.3f-%.3f (noise: %3.3f%%) (%d runs)' % (mean, std, median, min(times), max(times), 100*std/mean, reps) - print ' Native : mean: %.3f (+-%.3f) secs median: %.3f range: %.3f-%.3f (noise: %3.3f%%) JS is %.2f X slower' % (mean_native, std_native, median_native, min(native_times), max(native_times), 100*std_native/mean_native, final) - - def do_benchmark(self, name, src, expected_output='FAIL', args=[], emcc_args=[], native_args=[], shared_args=[], force_c=False, reps=TEST_REPS, native_exec=None, output_parser=None, args_processor=None): - args = args or [DEFAULT_ARG] - if args_processor: args = args_processor(args) - - dirname = self.get_dir() - filename = os.path.join(dirname, name + '.c' + ('' if force_c else 'pp')) - f = open(filename, 'w') - f.write(src) - f.close() - final_filename = os.path.join(dirname, name + '.js') - - open('hardcode.py', 'w').write(''' -def process(filename): - js = open(filename).read() - replaced = js.replace("run();", "run(%s.concat(Module[\\"arguments\\"]));") - assert js != replaced - open(filename, 'w').write(replaced) -import sys -process(sys.argv[1]) -''' % str(args[:-1]) # do not hardcode in the last argument, the default arg -) - - try_delete(final_filename) - output = Popen([PYTHON, EMCC, filename, #'-O3', - '-O2', '-s', 'DOUBLE_MODE=0', '-s', 'PRECISE_I64_MATH=0', - '--llvm-lto', '3', '--memory-init-file', '0', '--js-transform', 'python hardcode.py', - '-s', 'TOTAL_MEMORY=128*1024*1024', - '--closure', '1', - #'-g', - '-o', final_filename] + shared_args + emcc_args, stdout=PIPE, stderr=self.stderr_redirect).communicate() - assert os.path.exists(final_filename), 'Failed to compile file: ' + output[0] - - # Run JS - global total_times, tests_done - times = [] - for i in range(reps): - start = time.time() - js_output = run_js(final_filename, engine=JS_ENGINE, args=args, stderr=PIPE, full_output=True) - - if i == 0 and 'uccessfully compiled asm.js code' in js_output: - if 'asm.js link error' not in js_output: - print "[%s was asm.js'ified]" % name - if not output_parser: - curr = time.time()-start - else: - curr = output_parser(js_output) - times.append(curr) - total_times[tests_done] += curr - if i == 0: - # Sanity check on output - self.assertContained(expected_output, js_output) - - # Run natively - if not native_exec: - self.build_native(filename, shared_args + native_args) - else: - shutil.copyfile(native_exec, filename + '.native') - shutil.copymode(native_exec, filename + '.native') - global total_native_times - native_times = [] - for i in range(reps): - start = time.time() - native_output = self.run_native(filename, args) - if i == 0: - # Sanity check on output - self.assertContained(expected_output, native_output) - if not output_parser: - curr = time.time()-start - else: - curr = output_parser(native_output) - native_times.append(curr) - total_native_times[tests_done] += curr - - self.print_stats(times, native_times, reps=reps) - - #tests_done += 1 - #if tests_done == TOTAL_TESTS: - # print 'Total stats:', - # self.print_stats(total_times, total_native_times, last=True) - - def test_primes(self): - src = r''' - #include - #include - int main(int argc, char **argv) { - int arg = argc > 1 ? argv[1][0] - '0' : 3; - switch(arg) { - case 0: return 0; break; - case 1: arg = 33000; break; - case 2: arg = 130000; break; - case 3: arg = 220000; break; - case 4: arg = 610000; break; - case 5: arg = 1010000; break; - default: printf("error: %d\\n", arg); return -1; - } - - int primes = 0, curri = 2; - while (primes < arg) { - int ok = true; - for (int j = 2; j < sqrtf(curri); j++) { - if (curri % j == 0) { - ok = false; - break; - } - } - if (ok) { - primes++; - } - curri++; - } - printf("lastprime: %d.\n", curri-1); - return 0; - } - ''' - self.do_benchmark('primes', src, 'lastprime:') - - def test_memops(self): - src = ''' - #include - #include - #include - int main(int argc, char **argv) { - int N, M; - int arg = argc > 1 ? argv[1][0] - '0' : 3; - switch(arg) { - case 0: return 0; break; - case 1: N = 1024*1024; M = 55; break; - case 2: N = 1024*1024; M = 400; break; - case 3: N = 1024*1024; M = 800; break; - case 4: N = 1024*1024; M = 4000; break; - case 5: N = 1024*1024; M = 8000; break; - default: printf("error: %d\\n", arg); return -1; - } - - int final = 0; - char *buf = (char*)malloc(N); - for (int t = 0; t < M; t++) { - for (int i = 0; i < N; i++) - buf[i] = (i + final)%256; - for (int i = 0; i < N; i++) - final += buf[i] & 1; - final = final % 1000; - } - printf("final: %d.\\n", final); - return 0; - } - ''' - self.do_benchmark('memops', src, 'final:') - - def zzztest_files(self): - src = r''' - #include - #include - #include - #include - - int main() { - int N = 100; - int M = 1000; - int K = 1000; - unsigned char *k = (unsigned char*)malloc(K+1), *k2 = (unsigned char*)malloc(K+1); - for (int i = 0; i < K; i++) { - k[i] = (i % 250) + 1; - } - k[K] = 0; - char buf[100]; - for (int i = 0; i < N; i++) { - sprintf(buf, "/dev/shm/file-%d.dat", i); - FILE *f = fopen(buf, "w"); - for (int j = 0; j < M; j++) { - fwrite(k, 1, (j % K) + 1, f); - } - fclose(f); - } - for (int i = 0; i < N; i++) { - sprintf(buf, "/dev/shm/file-%d.dat", i); - FILE *f = fopen(buf, "r"); - for (int j = 0; j < M; j++) { - fread(k2, 1, (j % K) + 1, f); - } - fclose(f); - for (int j = 0; j < K; j++) { - assert(k[j] == k2[j]); - } - unlink(buf); - } - printf("ok"); - return 0; - } - ''' - self.do_benchmark(src, 'ok') - - def test_copy(self): - src = r''' - #include - struct vec { - int x, y, z; - int r, g, b; - vec(int x_, int y_, int z_, int r_, int g_, int b_) : x(x_), y(y_), z(z_), r(r_), g(g_), b(b_) {} - static vec add(vec a, vec b) { - return vec(a.x+b.x, a.y+b.y, a.z+b.z, a.r+b.r, a.g+b.g, a.b+b.b); - } - void norm() { - x %= 1024; - y %= 1024; - z %= 1024; - r %= 1024; - b %= 1024; - g %= 1024; - } - int sum() { return x + y + z + r + g + b; } - }; - int main(int argc, char **argv) { - int arg = argc > 1 ? argv[1][0] - '0' : 3; - switch(arg) { - case 0: return 0; break; - case 1: arg = 75; break; - case 2: arg = 625; break; - case 3: arg = 1250; break; - case 4: arg = 5*1250; break; - case 5: arg = 10*1250; break; - default: printf("error: %d\\n", arg); return -1; - } - - int total = 0; - for (int i = 0; i < arg; i++) { - for (int j = 0; j < 50000; j++) { - vec c(i, i+i%10, j*2, i%255, j%120, i%15); - vec d(j+i%10, j*2, j%255, i%120, j%15, j); - vec e = c; - c.norm(); - d.norm(); - vec f = vec::add(c, d); - f = vec::add(e, f); - f.norm(); - f = vec::add(d, f); - total += f.sum() % 100; - total %= 10240; - } - } - printf("sum:%d\n", total); - return 0; - } - ''' - self.do_benchmark('copy', src, 'sum:') - - def test_fannkuch(self): - src = open(path_from_root('tests', 'fannkuch.cpp'), 'r').read().replace( - 'int n = argc > 1 ? atoi(argv[1]) : 0;', - ''' - int n; - int arg = argc > 1 ? argv[1][0] - '0' : 3; - switch(arg) { - case 0: return 0; break; - case 1: n = 9; break; - case 2: n = 10; break; - case 3: n = 11; break; - case 4: n = 11; break; - case 5: n = 12; break; - default: printf("error: %d\\n", arg); return -1; - } - ''' - ) - assert 'switch(arg)' in src - self.do_benchmark('fannkuch', src, 'Pfannkuchen(') - - def test_corrections(self): - src = r''' - #include - #include - int main(int argc, char **argv) { - int N, M; - int arg = argc > 1 ? argv[1][0] - '0' : 3; - switch(arg) { - case 0: return 0; break; - case 1: N = 20000; M = 550; break; - case 2: N = 20000; M = 3500; break; - case 3: N = 20000; M = 7000; break; - case 4: N = 20000; M = 5*7000; break; - case 5: N = 20000; M = 10*7000; break; - default: printf("error: %d\\n", arg); return -1; - } - - unsigned int f = 0; - unsigned short s = 0; - for (int t = 0; t < M; t++) { - for (int i = 0; i < N; i++) { - f += i / ((t % 5)+1); - if (f > 1000) f /= (t % 3)+1; - if (i % 4 == 0) f += i * (i % 8 == 0 ? 1 : -1); - s += (short(f)*short(f)) % 256; - } - } - printf("final: %d:%d.\n", f, s); - return 0; - } - ''' - self.do_benchmark('corrections', src, 'final:', emcc_args=['-s', 'CORRECT_SIGNS=1', '-s', 'CORRECT_OVERFLOWS=1', '-s', 'CORRECT_ROUNDINGS=1']) - - def fasta(self, name, double_rep, emcc_args=[]): - src = open(path_from_root('tests', 'fasta.cpp'), 'r').read().replace('double', double_rep) - src = src.replace(' const size_t n = ( argc > 1 ) ? atoi( argv[1] ) : 512;', ''' - int n; - int arg = argc > 1 ? argv[1][0] - '0' : 3; - switch(arg) { - case 0: return 0; break; - case 1: n = 19000000/20; break; - case 2: n = 19000000/2; break; - case 3: n = 19000000; break; - case 4: n = 19000000*5; break; - case 5: n = 19000000*10; break; - default: printf("error: %d\\n", arg); return -1; - } - ''') - assert 'switch(arg)' in src - self.do_benchmark('fasta', src, '') - - def test_fasta_float(self): - self.fasta('fasta_float', 'float') - - def test_fasta_double(self): - self.fasta('fasta_double', 'double') - - def test_fasta_double_full(self): - self.fasta('fasta_double_full', 'double', emcc_args=['-s', 'DOUBLE_MODE=1']) - - def test_skinning(self): - src = open(path_from_root('tests', 'skinning_test_no_simd.cpp'), 'r').read() - self.do_benchmark('skinning', src, 'blah=0.000000') - - def test_life(self): - src = open(path_from_root('tests', 'life.c'), 'r').read() - self.do_benchmark('life', src, '''--------------------------------''', shared_args=['-std=c99'], force_c=True) - - def test_linpack(self): - def output_parser(output): - return 100.0/float(re.search('Unrolled Double Precision +([\d\.]+) Mflops', output).group(1)) - self.do_benchmark('linpack', open(path_from_root('tests', 'linpack.c')).read(), '''Unrolled Double Precision''', force_c=True, output_parser=output_parser) - - def test_zzz_java_nbody(self): # tests xmlvm compiled java, including bitcasts of doubles, i64 math, etc. - args = [path_from_root('tests', 'nbody-java', x) for x in os.listdir(path_from_root('tests', 'nbody-java')) if x.endswith('.c')] + \ - ['-I' + path_from_root('tests', 'nbody-java')] - self.do_benchmark('nbody_java', '', '''Time(s)''', - force_c=True, emcc_args=args + ['-s', 'PRECISE_I64_MATH=1', '--llvm-lto', '2'], native_args=args + ['-lgc', '-std=c99', '-target', 'x86_64-pc-linux-gnu', '-lm']) - - def lua(self, benchmark, expected, output_parser=None, args_processor=None): - shutil.copyfile(path_from_root('tests', 'lua', benchmark + '.lua'), benchmark + '.lua') - #shutil.copyfile(path_from_root('tests', 'lua', 'binarytrees.lua'), 'binarytrees.lua') - #shutil.copyfile(path_from_root('tests', 'lua', 'scimark.lua'), 'scimark.lua') - emcc_args = self.get_library('lua', [os.path.join('src', 'lua'), os.path.join('src', 'liblua.a')], make=['make', 'generic'], configure=None) + \ - ['--embed-file', benchmark + '.lua'] - #['--embed-file', 'binarytrees.lua', '--embed-file', 'scimark.lua'] + ['--minify', '0'] - shutil.copyfile(emcc_args[0], emcc_args[0] + '.bc') - emcc_args[0] += '.bc' - native_args = self.get_library('lua_native', [os.path.join('src', 'lua'), os.path.join('src', 'liblua.a')], make=['make', 'generic'], configure=None, native=True) - - self.do_benchmark('lua_' + benchmark, '', expected, - force_c=True, args=[benchmark + '.lua', DEFAULT_ARG], emcc_args=emcc_args, native_args=native_args, native_exec=os.path.join('building', 'lua_native', 'src', 'lua'), - output_parser=output_parser, args_processor=args_processor) - - def test_zzz_lua_scimark(self): - def output_parser(output): - return 100.0/float(re.search('\nSciMark +([\d\.]+) ', output).group(1)) - - self.lua('scimark', '[small problem sizes]', output_parser=output_parser) - - def test_zzz_lua_binarytrees(self): - # js version: ['binarytrees.lua', {0: 0, 1: 9.5, 2: 11.99, 3: 12.85, 4: 14.72, 5: 15.82}[arguments[0]]] - self.lua('binarytrees', 'long lived tree of depth') - - def test_zzz_zlib(self): - src = open(path_from_root('tests', 'zlib', 'benchmark.c'), 'r').read() - emcc_args = self.get_library('zlib', os.path.join('libz.a'), make_args=['libz.a']) + \ - ['-I' + path_from_root('tests', 'zlib')] - native_args = self.get_library('zlib_native', os.path.join('libz.a'), make_args=['libz.a'], native=True) + \ - ['-I' + path_from_root('tests', 'zlib')] - self.do_benchmark('zlib', src, '''ok.''', - force_c=True, emcc_args=emcc_args, native_args=native_args) - - def test_zzz_box2d(self): # Called thus so it runs late in the alphabetical cycle... it is long - src = open(path_from_root('tests', 'box2d', 'Benchmark.cpp'), 'r').read() - - js_lib = self.get_library('box2d', [os.path.join('box2d.a')], configure=None) - native_lib = self.get_library('box2d_native', [os.path.join('box2d.a')], configure=None, native=True) - - emcc_args = js_lib + ['-I' + path_from_root('tests', 'box2d')] - native_args = native_lib + ['-I' + path_from_root('tests', 'box2d')] - - self.do_benchmark('box2d', src, 'frame averages', emcc_args=emcc_args, native_args=native_args) - - def test_zzz_bullet(self): # Called thus so it runs late in the alphabetical cycle... it is long - src = open(path_from_root('tests', 'bullet', 'Demos', 'Benchmarks', 'BenchmarkDemo.cpp'), 'r').read() + \ - open(path_from_root('tests', 'bullet', 'Demos', 'Benchmarks', 'main.cpp'), 'r').read() - - js_lib = self.get_library('bullet', [os.path.join('src', '.libs', 'libBulletDynamics.a'), - os.path.join('src', '.libs', 'libBulletCollision.a'), - os.path.join('src', '.libs', 'libLinearMath.a')], - configure_args=['--disable-demos','--disable-dependency-tracking']) - native_lib = self.get_library('bullet_native', [os.path.join('src', '.libs', 'libBulletDynamics.a'), - os.path.join('src', '.libs', 'libBulletCollision.a'), - os.path.join('src', '.libs', 'libLinearMath.a')], - configure_args=['--disable-demos','--disable-dependency-tracking'], - native=True) - - emcc_args = js_lib + ['-I' + path_from_root('tests', 'bullet', 'src'), - '-I' + path_from_root('tests', 'bullet', 'Demos', 'Benchmarks'), - '-s', 'DEAD_FUNCTIONS=["__ZSt9terminatev"]'] - native_args = native_lib + ['-I' + path_from_root('tests', 'bullet', 'src'), - '-I' + path_from_root('tests', 'bullet', 'Demos', 'Benchmarks')] - - self.do_benchmark('bullet', src, '\nok.\n', emcc_args=emcc_args, native_args=native_args) - -elif 'sanity' in str(sys.argv): - - # Run some sanity checks on the test runner and emcc. - - sys.argv = filter(lambda x: x != 'sanity', sys.argv) - - print - print 'Running sanity checks.' - print 'WARNING: This will modify %s, and in theory can break it although it should be restored properly. A backup will be saved in %s_backup' % (EM_CONFIG, EM_CONFIG) - print - - assert os.path.exists(CONFIG_FILE), 'To run these tests, we need a (working!) %s file to already exist' % EM_CONFIG - - assert not os.environ.get('EMCC_DEBUG'), 'do not run sanity checks in debug mode!' - - shutil.copyfile(CONFIG_FILE, CONFIG_FILE + '_backup') - def restore(): - shutil.copyfile(CONFIG_FILE + '_backup', CONFIG_FILE) - - SANITY_FILE = CONFIG_FILE + '_sanity' - - def wipe(): - try_delete(CONFIG_FILE) - try_delete(SANITY_FILE) - - commands = [[EMCC], [PYTHON, path_from_root('tests', 'runner.py'), 'blahblah']] - - def mtime(filename): - return os.stat(filename).st_mtime - - class sanity(RunnerCore): - def setUp(self): - wipe() - - def tearDown(self): - restore() - - def do(self, command): - if type(command) is not list: - command = [command] - if command[0] == EMCC: - command = [PYTHON] + command - - return Popen(command, stdout=PIPE, stderr=STDOUT).communicate()[0] - - def check_working(self, command, expected=None): - if type(command) is not list: - command = [command] - if expected is None: - if command[0] == EMCC: - expected = 'no input files' - else: - expected = "has no attribute 'blahblah'" - - output = self.do(command) - self.assertContained(expected, output) - return output - - def test_aaa_normal(self): # this should be the very first thing that runs. if this fails, everything else is irrelevant! - for command in commands: - # Your existing EM_CONFIG should work! - restore() - self.check_working(command) - - def test_firstrun(self): - for command in commands: - wipe() - - def make_executable(name): - with open(os.path.join(temp_bin, name), 'w') as f: - os.fchmod(f.fileno(), stat.S_IRWXU) - - try: - temp_bin = tempfile.mkdtemp() - old_environ_path = os.environ['PATH'] - os.environ['PATH'] = temp_bin + os.pathsep + old_environ_path - make_executable('llvm-dis') - make_executable('node') - make_executable('python2') - output = self.do(command) - finally: - os.environ['PATH'] = old_environ_path - shutil.rmtree(temp_bin) - - self.assertContained('Welcome to Emscripten!', output) - self.assertContained('This is the first time any of the Emscripten tools has been run.', output) - self.assertContained('A settings file has been copied to %s, at absolute path: %s' % (EM_CONFIG, CONFIG_FILE), output) - self.assertContained('It contains our best guesses for the important paths, which are:', output) - self.assertContained('LLVM_ROOT', output) - self.assertContained('NODE_JS', output) - self.assertContained('PYTHON', output) - if platform.system() is not 'Windows': - # os.chmod can't make files executable on Windows - self.assertIdentical(temp_bin, re.search("^ *LLVM_ROOT *= (.*)$", output, re.M).group(1)) - self.assertIdentical(os.path.join(temp_bin, 'node'), re.search("^ *NODE_JS *= (.*)$", output, re.M).group(1)) - self.assertIdentical(os.path.join(temp_bin, 'python2'), re.search("^ *PYTHON *= (.*)$", output, re.M).group(1)) - self.assertContained('Please edit the file if any of those are incorrect', output) - self.assertContained('This command will now exit. When you are done editing those paths, re-run it.', output) - assert output.split()[-1].endswith('===='), 'We should have stopped: ' + output - config_file = open(CONFIG_FILE).read() - template_file = open(path_from_root('tools', 'settings_template_readonly.py')).read() - self.assertNotContained('~/.emscripten', config_file) - self.assertContained('~/.emscripten', template_file) - self.assertNotContained('{{{', config_file) - self.assertNotContained('}}}', config_file) - self.assertContained('{{{', template_file) - self.assertContained('}}}', template_file) - for content in ['EMSCRIPTEN_ROOT', 'LLVM_ROOT', 'NODE_JS', 'TEMP_DIR', 'COMPILER_ENGINE', 'JS_ENGINES']: - self.assertContained(content, config_file) - - # The guessed config should be ok XXX This depends on your local system! it is possible `which` guesses wrong - #try_delete('a.out.js') - #output = Popen([PYTHON, EMCC, path_from_root('tests', 'hello_world.c')], stdout=PIPE, stderr=PIPE).communicate() - #self.assertContained('hello, world!', run_js('a.out.js'), output) - - # Second run, with bad EM_CONFIG - for settings in ['blah', 'LLVM_ROOT="blarg"; JS_ENGINES=[]; COMPILER_ENGINE=NODE_JS=SPIDERMONKEY_ENGINE=[]']: - f = open(CONFIG_FILE, 'w') - f.write(settings) - f.close() - output = self.do(command) - - if 'LLVM_ROOT' not in settings: - self.assertContained('Error in evaluating %s' % EM_CONFIG, output) - elif 'runner.py' not in ' '.join(command): - self.assertContained('CRITICAL', output) # sanity check should fail - - def test_closure_compiler(self): - CLOSURE_FATAL = 'fatal: Closure compiler' - CLOSURE_WARNING = 'does not exist' - - # Sanity check should find closure - restore() - output = self.check_working(EMCC) - self.assertNotContained(CLOSURE_FATAL, output) - self.assertNotContained(CLOSURE_WARNING, output) - - # Append a bad path for closure, will warn - f = open(CONFIG_FILE, 'a') - f.write('CLOSURE_COMPILER = "/tmp/nowhere/nothingtoseehere/kjadsfkjwelkjsdfkqgas/nonexistent.txt"\n') - f.close() - output = self.check_working(EMCC, CLOSURE_WARNING) - - # And if you actually try to use the bad path, will be fatal - f = open(CONFIG_FILE, 'a') - f.write('CLOSURE_COMPILER = "/tmp/nowhere/nothingtoseehere/kjadsfkjwelkjsdfkqgas/nonexistent.txt"\n') - f.close() - output = self.check_working([EMCC, '-O2', '-s', 'ASM_JS=0', '--closure', '1', 'tests/hello_world.cpp'], CLOSURE_FATAL) - - # With a working path, all is well - restore() - try_delete('a.out.js') - output = self.check_working([EMCC, '-O2', '-s', 'ASM_JS=0', '--closure', '1', 'tests/hello_world.cpp'], '') - assert os.path.exists('a.out.js'), output - - def test_llvm(self): - LLVM_WARNING = 'LLVM version appears incorrect' - - restore() - - # Clang should report the version number we expect, and emcc should not warn - assert check_clang_version() - output = self.check_working(EMCC) - assert LLVM_WARNING not in output, output - - # Fake a different llvm version - restore() - f = open(CONFIG_FILE, 'a') - f.write('LLVM_ROOT = "' + path_from_root('tests', 'fake') + '"') - f.close() - - if not os.path.exists(path_from_root('tests', 'fake')): - os.makedirs(path_from_root('tests', 'fake')) - - try: - os.environ['EM_IGNORE_SANITY'] = '1' - for x in range(-2, 3): - for y in range(-2, 3): - f = open(path_from_root('tests', 'fake', 'clang'), 'w') - f.write('#!/bin/sh\n') - f.write('echo "clang version %d.%d" 1>&2\n' % (EXPECTED_LLVM_VERSION[0] + x, EXPECTED_LLVM_VERSION[1] + y)) - f.close() - shutil.copyfile(path_from_root('tests', 'fake', 'clang'), path_from_root('tests', 'fake', 'clang++')) - os.chmod(path_from_root('tests', 'fake', 'clang'), stat.S_IREAD | stat.S_IWRITE | stat.S_IEXEC) - os.chmod(path_from_root('tests', 'fake', 'clang++'), stat.S_IREAD | stat.S_IWRITE | stat.S_IEXEC) - if x != 0 or y != 0: - output = self.check_working(EMCC, LLVM_WARNING) - else: - output = self.check_working(EMCC) - assert LLVM_WARNING not in output, output - finally: - del os.environ['EM_IGNORE_SANITY'] - - def test_node(self): - NODE_WARNING = 'node version appears too old' - NODE_WARNING_2 = 'cannot check node version' - - restore() - - # Clang should report the version number we expect, and emcc should not warn - assert check_node_version() - output = self.check_working(EMCC) - assert NODE_WARNING not in output, output - - # Fake a different node version - restore() - f = open(CONFIG_FILE, 'a') - f.write('NODE_JS = "' + path_from_root('tests', 'fake', 'nodejs') + '"') - f.close() - - if not os.path.exists(path_from_root('tests', 'fake')): - os.makedirs(path_from_root('tests', 'fake')) - - try: - os.environ['EM_IGNORE_SANITY'] = '1' - for version, succeed in [('v0.7.9', False), ('v0.8.0', True), ('v0.8.1', True), ('cheez', False)]: - f = open(path_from_root('tests', 'fake', 'nodejs'), 'w') - f.write('#!/bin/sh\n') - f.write('''if [ $1 = "--version" ]; then - echo "%s" -else - %s $@ -fi -''' % (version, NODE_JS)) - f.close() - os.chmod(path_from_root('tests', 'fake', 'nodejs'), stat.S_IREAD | stat.S_IWRITE | stat.S_IEXEC) - if not succeed: - if version[0] == 'v': - self.check_working(EMCC, NODE_WARNING) - else: - self.check_working(EMCC, NODE_WARNING_2) - else: - output = self.check_working(EMCC) - assert NODE_WARNING not in output, output - finally: - del os.environ['EM_IGNORE_SANITY'] - - def test_emcc(self): - SANITY_MESSAGE = 'Emscripten: Running sanity checks' - SANITY_FAIL_MESSAGE = 'sanity check failed to run' - - # emcc should check sanity if no ${EM_CONFIG}_sanity - restore() - time.sleep(0.1) - assert not os.path.exists(SANITY_FILE) # restore is just the settings, not the sanity - output = self.check_working(EMCC) - self.assertContained(SANITY_MESSAGE, output) - assert os.path.exists(SANITY_FILE) # EMCC should have checked sanity successfully - assert mtime(SANITY_FILE) >= mtime(CONFIG_FILE) - assert generate_sanity() == open(SANITY_FILE).read() - self.assertNotContained(SANITY_FAIL_MESSAGE, output) - - # emcc run again should not sanity check, because the sanity file is newer - output = self.check_working(EMCC) - self.assertNotContained(SANITY_MESSAGE, output) - self.assertNotContained(SANITY_FAIL_MESSAGE, output) - - # correct sanity contents mean we need not check - open(SANITY_FILE, 'w').write(generate_sanity()) - output = self.check_working(EMCC) - self.assertNotContained(SANITY_MESSAGE, output) - - # incorrect sanity contents mean we *must* check - open(SANITY_FILE, 'w').write('wakawaka') - output = self.check_working(EMCC) - self.assertContained(SANITY_MESSAGE, output) - - # but with EMCC_DEBUG=1 we should check - try: - os.environ['EMCC_DEBUG'] = '1' - output = self.check_working(EMCC) - finally: - del os.environ['EMCC_DEBUG'] - self.assertContained(SANITY_MESSAGE, output) - output = self.check_working(EMCC) - self.assertNotContained(SANITY_MESSAGE, output) - - # Make sure the test runner didn't do anything to the setup - output = self.check_working(EMCC) - self.assertNotContained(SANITY_MESSAGE, output) - self.assertNotContained(SANITY_FAIL_MESSAGE, output) - - # emcc should also check sanity if the file is outdated - time.sleep(0.1) - restore() - assert mtime(SANITY_FILE) < mtime(CONFIG_FILE) - output = self.check_working(EMCC) - self.assertContained(SANITY_MESSAGE, output) - assert mtime(SANITY_FILE) >= mtime(CONFIG_FILE) - self.assertNotContained(SANITY_FAIL_MESSAGE, output) - - # emcc should be configurable directly from EM_CONFIG without any config file - restore() - config = open(CONFIG_FILE, 'r').read() - os.environ['EM_CONFIG'] = config - wipe() - dirname = tempfile.mkdtemp(prefix='emscripten_test_' + self.__class__.__name__ + '_', dir=TEMP_DIR) - open(os.path.join(dirname, 'main.cpp'), 'w').write(''' - #include - int main() { - printf("hello from emcc with no config file\\n"); - return 0; - } - ''') - Popen([PYTHON, EMCC, os.path.join(dirname, 'main.cpp'), '-o', os.path.join(dirname, 'a.out.js')]).communicate() - del os.environ['EM_CONFIG'] - old_dir = os.getcwd() - try: - os.chdir(dirname) - self.assertContained('hello from emcc with no config file', run_js('a.out.js')) - finally: - os.chdir(old_dir) - shutil.rmtree(dirname) - - try_delete(CANONICAL_TEMP_DIR) - - def test_emcc_caching(self): - INCLUDING_MESSAGE = 'including X' - BUILDING_MESSAGE = 'building X for cache' - ERASING_MESSAGE = 'clearing cache' - - EMCC_CACHE = Cache.dirname - - for compiler in [EMCC, EMXX]: - print compiler - - restore() - - Cache.erase() - assert not os.path.exists(EMCC_CACHE) - - try: - os.environ['EMCC_DEBUG'] ='1' - self.working_dir = os.path.join(TEMP_DIR, 'emscripten_temp') - - # Building a file that doesn't need cached stuff should not trigger cache generation - output = self.do([compiler, path_from_root('tests', 'hello_world.cpp')]) - assert INCLUDING_MESSAGE.replace('X', 'libc') not in output - assert BUILDING_MESSAGE.replace('X', 'libc') not in output - self.assertContained('hello, world!', run_js('a.out.js')) - assert not os.path.exists(EMCC_CACHE) - try_delete('a.out.js') - - basebc_name = os.path.join(TEMP_DIR, 'emscripten_temp', 'emcc-0-basebc.bc') - dcebc_name = os.path.join(TEMP_DIR, 'emscripten_temp', 'emcc-1-linktime.bc') - ll_names = [os.path.join(TEMP_DIR, 'emscripten_temp', 'emcc-X-ll.ll').replace('X', str(x)) for x in range(2,5)] - - # Building a file that *does* need dlmalloc *should* trigger cache generation, but only the first time - for filename, libname in [('hello_malloc.cpp', 'libc'), ('hello_libcxx.cpp', 'libcxx')]: - for i in range(3): - print filename, libname, i - self.clear() - try_delete(basebc_name) # we might need to check this file later - try_delete(dcebc_name) # we might need to check this file later - for ll_name in ll_names: try_delete(ll_name) - output = self.do([compiler, '-O' + str(i), '-s', 'RELOOP=0', '--llvm-lto', '0', path_from_root('tests', filename)]) - #print output - assert INCLUDING_MESSAGE.replace('X', libname) in output - if libname == 'libc': - assert INCLUDING_MESSAGE.replace('X', 'libcxx') not in output # we don't need libcxx in this code - else: - assert INCLUDING_MESSAGE.replace('X', 'libc') in output # libcxx always forces inclusion of libc - assert (BUILDING_MESSAGE.replace('X', libname) in output) == (i == 0), 'Must only build the first time' - self.assertContained('hello, world!', run_js('a.out.js')) - assert os.path.exists(EMCC_CACHE) - assert os.path.exists(os.path.join(EMCC_CACHE, libname + '.bc')) - if libname == 'libcxx': - print os.stat(os.path.join(EMCC_CACHE, libname + '.bc')).st_size, os.stat(basebc_name).st_size, os.stat(dcebc_name).st_size - assert os.stat(os.path.join(EMCC_CACHE, libname + '.bc')).st_size > 1000000, 'libc++ is big' - assert os.stat(basebc_name).st_size > 1000000, 'libc++ is indeed big' - assert os.stat(dcebc_name).st_size < os.stat(basebc_name).st_size/2, 'Dead code elimination must remove most of libc++' - # should only have metadata in -O0, not 1 and 2 - if i > 0: - for ll_name in ll_names: - ll = None - try: - ll = open(ll_name).read() - break - except: - pass - assert ll - assert ll.count('\n!') < 10 # a few lines are left even in -O1 and -O2 - finally: - del os.environ['EMCC_DEBUG'] - - restore() - - def ensure_cache(): - self.do([EMCC, '-O2', path_from_root('tests', 'hello_world.c')]) - - # Manual cache clearing - ensure_cache() - assert os.path.exists(EMCC_CACHE) - output = self.do([EMCC, '--clear-cache']) - assert ERASING_MESSAGE in output - assert not os.path.exists(EMCC_CACHE) - - # Changing LLVM_ROOT, even without altering .emscripten, clears the cache - ensure_cache() - old = os.environ.get('LLVM') - try: - os.environ['LLVM'] = 'waka' - assert os.path.exists(EMCC_CACHE) - output = self.do([EMCC]) - assert ERASING_MESSAGE in output - assert not os.path.exists(EMCC_CACHE) - finally: - if old: os.environ['LLVM'] = old - else: del os.environ['LLVM'] - - try_delete(CANONICAL_TEMP_DIR) - - def test_relooper(self): - RELOOPER = Cache.get_path('relooper.js') - - restore() - for phase in range(2): # 0: we wipe the relooper dir. 1: we have it, so should just update - if phase == 0: Cache.erase() - try_delete(RELOOPER) - - for i in range(4): - print >> sys.stderr, phase, i - opt = min(i, 2) - try_delete('a.out.js') - output = Popen([PYTHON, EMCC, path_from_root('tests', 'hello_world_loop.cpp'), '-O' + str(opt), '-g'], - stdout=PIPE, stderr=PIPE).communicate() - self.assertContained('hello, world!', run_js('a.out.js')) - output = '\n'.join(output) - assert ('bootstrapping relooper succeeded' in output) == (i == 2), 'only bootstrap on first O2: ' + output - assert os.path.exists(RELOOPER) == (i >= 2), 'have relooper on O2: ' + output - src = open('a.out.js').read() - main = src.split('function _main()')[1].split('\n}\n')[0] - assert ('while (1) {' in main or 'while(1){' in main or '} while ($' in main or '}while($' in main) == (i >= 2), 'reloop code on O2: ' + main - assert ('switch' not in main) == (i >= 2), 'reloop code on O2: ' + main - - def test_jcache(self): - PRE_LOAD_MSG = 'loading pre from jcache' - PRE_SAVE_MSG = 'saving pre to jcache' - FUNC_CHUNKS_LOAD_MSG = ' funcchunks from jcache' - FUNC_CHUNKS_SAVE_MSG = ' funcchunks to jcache' - JSFUNC_CHUNKS_LOAD_MSG = 'jsfuncchunks from jcache' - JSFUNC_CHUNKS_SAVE_MSG = 'jsfuncchunks to jcache' - - restore() - Cache.erase() - - try: - os.environ['EMCC_DEBUG'] = '1' - os.environ['EMCC_JSOPT_MIN_CHUNK_SIZE'] = str(1024*512) - - self.working_dir = os.path.join(TEMP_DIR, 'emscripten_temp') - if not os.path.exists(self.working_dir): os.makedirs(self.working_dir) - - assert not os.path.exists(JCache.get_cachename('emscript_files')) - - srcs = {} - used_jcache = False - - for args, input_file, expect_pre_save, expect_pre_load, expect_funcs_save, expect_funcs_load, expect_jsfuncs_save, expect_jsfuncs_load, expected in [ - ([], 'hello_world_loop.cpp', False, False, False, False, False, False, []), - (['--jcache'], 'hello_world_loop.cpp', True, False, True, False, True, False, []), - (['--jcache'], 'hello_world_loop.cpp', False, True, False, True, False, True, []), - ([], 'hello_world_loop.cpp', False, False, False, False, False, False, []), - # new - ([], 'hello_world.cpp', False, False, False, False, False, False, []), - (['--jcache'], 'hello_world.cpp', True, False, True, False, True, False, []), - (['--jcache'], 'hello_world.cpp', False, True, False, True, False, True, []), - ([], 'hello_world.cpp', False, False, False, False, False, False, []), - # go back to old file, experience caching - (['--jcache'], 'hello_world_loop.cpp', False, True, False, True, False, True, []), - # new, large file - ([], 'hello_malloc.cpp', False, False, False, False, False, False, []), - (['--jcache'], 'hello_malloc.cpp', True, False, True, False, True, False, []), - (['--jcache'], 'hello_malloc.cpp', False, True, False, True, False, True, []), - ([], 'hello_malloc.cpp', False, False, False, False, False, False, []), - # new, huge file - ([], 'hello_libcxx.cpp', False, False, False, False, False, False, ('3 chunks',)), - (['--jcache'], 'hello_libcxx.cpp', True, False, True, False, True, False, []), - (['--jcache'], 'hello_libcxx.cpp', False, True, False, True, False, True, []), - ([], 'hello_libcxx.cpp', False, False, False, False, False, False, []), - # finally, build a file close to the previous, to see that some chunks are found in the cache and some not - (['--jcache'], 'hello_libcxx_mod1.cpp', False, True, True, True, True, True, []), # win on pre, mix on funcs, mix on jsfuncs - (['--jcache'], 'hello_libcxx_mod1.cpp', False, True, False, True, False, True, []), - (None, None, None, None, None, None, None, None, None), # clear - (['--jcache'], 'hello_libcxx_mod2.cpp', True, False, True, False, True, False, []), # load into cache - (['--jcache'], 'hello_libcxx_mod2a.cpp', False, True, True, True, True, True, []) # add a printf, do not lose everything - ]: - self.clear() - if args is None: - Cache.erase() - continue - - print >> sys.stderr, args, input_file, expect_pre_save, expect_pre_load, expect_funcs_save, expect_funcs_load, expect_jsfuncs_save, expect_jsfuncs_load, expected - - out, err = Popen([PYTHON, EMCC, '-O2', '-g', path_from_root('tests', input_file)] + args, stdout=PIPE, stderr=PIPE).communicate() - errtail = err.split('emcc invocation')[-1] - self.assertContained('hello, world!', run_js('a.out.js'), errtail) - assert (PRE_SAVE_MSG in err) == expect_pre_save, errtail - assert (PRE_LOAD_MSG in err) == expect_pre_load, errtail - assert (FUNC_CHUNKS_SAVE_MSG in err) == expect_funcs_save, errtail - assert (FUNC_CHUNKS_LOAD_MSG in err) == expect_funcs_load, errtail - assert (JSFUNC_CHUNKS_SAVE_MSG in err) == expect_jsfuncs_save, errtail - assert (JSFUNC_CHUNKS_LOAD_MSG in err) == expect_jsfuncs_load, errtail - for expect in expected: assert expect in err, expect + ' ? ' + errtail - curr = open('a.out.js').read() - if input_file not in srcs: - srcs[input_file] = curr - else: - #open('/home/alon/Dev/emscripten/a', 'w').write(srcs[input_file]) - #open('/home/alon/Dev/emscripten/b', 'w').write(curr) - assert abs(len(curr)/float(len(srcs[input_file]))-1)<0.01, 'contents may shift in order, but must remain the same size %d vs %d' % (len(curr), len(srcs[input_file])) + '\n' + errtail - used_jcache = used_jcache or ('--jcache' in args) - assert used_jcache == os.path.exists(JCache.get_cachename('emscript_files')) - #print >> sys.stderr, errtail - - finally: - del os.environ['EMCC_DEBUG'] - del os.environ['EMCC_JSOPT_MIN_CHUNK_SIZE'] - -else: - raise Exception('Test runner is confused: ' + str(sys.argv)) - -if __name__ == '__main__': - # Sanity checks - - total_engines = len(JS_ENGINES) - JS_ENGINES = filter(check_engine, JS_ENGINES) - if len(JS_ENGINES) == 0: - print 'WARNING: None of the JS engines in JS_ENGINES appears to work.' - elif len(JS_ENGINES) < total_engines: - print 'WARNING: Not all the JS engines in JS_ENGINES appears to work, ignoring those.' - - # Skip requested tests - - for i in range(len(sys.argv)): - arg = sys.argv[i] - if arg.startswith('skip:'): - which = arg.split('skip:')[1] - if which.startswith('ALL.'): - ignore, test = which.split('.') - which = map(lambda mode: mode+'.'+test, test_modes) - else: - which = [which] - - print >> sys.stderr, ','.join(which) - for test in which: - print >> sys.stderr, 'will skip "%s"' % test - exec(test + ' = RunnerCore.skipme') - - sys.argv[i] = '' - sys.argv = filter(lambda arg: arg, sys.argv) - - # Go - - unittest.main(verbosity=2) + # Create a list of modules to load tests from + modules = [] + for filename in glob.glob(os.path.join(os.path.dirname(__file__), 'test*.py')): + module_dir, module_file = os.path.split(filename) + module_name, module_ext = os.path.splitext(module_file) + __import__(module_name) + modules.append(sys.modules[module_name]) + + # Filter and load tests from the discovered modules + loader = unittest.TestLoader() + names = sys.argv[1:] + suites = [] + for m in modules: + try: + suites.append(loader.loadTestsFromNames(names, m)) + except: + pass + + # Run the discovered tests + if not len(suites): + print >> sys.stderr, 'No tests found for %s' % str(sys.argv[1:]) + else: + testRunner = unittest.TextTestRunner(verbosity=2) + for suite in suites: + testRunner.run(suite) diff --git a/tests/test_benchmark.py b/tests/test_benchmark.py new file mode 100644 index 0000000000000..6efc1f638c891 --- /dev/null +++ b/tests/test_benchmark.py @@ -0,0 +1,507 @@ +import math, os, shutil, subprocess +import runner +from runner import RunnerCore, path_from_root +from tools.shared import * + +# standard arguments for timing: +# 0: no runtime, just startup +# 1: very little runtime +# 2: 0.5 seconds +# 3: 1 second +# 4: 5 seconds +# 5: 10 seconds +DEFAULT_ARG = '4' + +TEST_REPS = 2 +TOTAL_TESTS = 8 + +tests_done = 0 +total_times = map(lambda x: 0., range(TOTAL_TESTS)) +total_native_times = map(lambda x: 0., range(TOTAL_TESTS)) + +class benchmark(RunnerCore): + save_dir = True + + @classmethod + def setUpClass(self): + super(benchmark, self).setUpClass() + + fingerprint = [time.asctime()] + try: + fingerprint.append('em: ' + Popen(['git', 'show'], stdout=PIPE).communicate()[0].split('\n')[0]) + except: + pass + try: + d = os.getcwd() + os.chdir(os.path.expanduser('~/Dev/mozilla-central')) + fingerprint.append('sm: ' + filter(lambda line: 'changeset' in line, + Popen(['hg', 'tip'], stdout=PIPE).communicate()[0].split('\n'))[0]) + except: + pass + finally: + os.chdir(d) + fingerprint.append('llvm: ' + LLVM_ROOT) + print 'Running Emscripten benchmarks... [ %s ]' % ' | '.join(fingerprint) + + assert(os.path.exists(CLOSURE_COMPILER)) + + try: + index = SPIDERMONKEY_ENGINE.index("options('strict')") + SPIDERMONKEY_ENGINE = SPIDERMONKEY_ENGINE[:index-1] + SPIDERMONKEY_ENGINE[index+1:] # closure generates non-strict + except: + pass + + Building.COMPILER = CLANG + Building.COMPILER_TEST_OPTS = [] + + # Pick the JS engine to benchmark. If you specify one, it will be picked. For example, python tests/runner.py benchmark SPIDERMONKEY_ENGINE + global JS_ENGINE + JS_ENGINE = runner.js_engine_override if runner.js_engine_override is not None else JS_ENGINES[0] + print 'Benchmarking JS engine: %s' % JS_ENGINE + + def print_stats(self, times, native_times, last=False, reps=TEST_REPS): + if reps == 0: + print '(no reps)' + return + mean = sum(times)/len(times) + squared_times = map(lambda x: x*x, times) + mean_of_squared = sum(squared_times)/len(times) + std = math.sqrt(mean_of_squared - mean*mean) + sorted_times = times[:] + sorted_times.sort() + median = sum(sorted_times[len(sorted_times)/2 - 1:len(sorted_times)/2 + 1])/2 + + mean_native = sum(native_times)/len(native_times) + squared_native_times = map(lambda x: x*x, native_times) + mean_of_squared_native = sum(squared_native_times)/len(native_times) + std_native = math.sqrt(mean_of_squared_native - mean_native*mean_native) + sorted_native_times = native_times[:] + sorted_native_times.sort() + median_native = sum(sorted_native_times[len(sorted_native_times)/2 - 1:len(sorted_native_times)/2 + 1])/2 + + final = mean / mean_native + + if last: + norm = 0 + for i in range(len(times)): + norm += times[i]/native_times[i] + norm /= len(times) + print + print ' JavaScript: %.3f Native: %.3f Ratio: %.3f Normalized ratio: %.3f' % (mean, mean_native, final, norm) + return + + print + print ' JavaScript: mean: %.3f (+-%.3f) secs median: %.3f range: %.3f-%.3f (noise: %3.3f%%) (%d runs)' % (mean, std, median, min(times), max(times), 100*std/mean, reps) + print ' Native : mean: %.3f (+-%.3f) secs median: %.3f range: %.3f-%.3f (noise: %3.3f%%) JS is %.2f X slower' % (mean_native, std_native, median_native, min(native_times), max(native_times), 100*std_native/mean_native, final) + + def do_benchmark(self, name, src, expected_output='FAIL', args=[], emcc_args=[], native_args=[], shared_args=[], force_c=False, reps=TEST_REPS, native_exec=None, output_parser=None, args_processor=None): + args = args or [DEFAULT_ARG] + if args_processor: args = args_processor(args) + + dirname = self.get_dir() + filename = os.path.join(dirname, name + '.c' + ('' if force_c else 'pp')) + f = open(filename, 'w') + f.write(src) + f.close() + final_filename = os.path.join(dirname, name + '.js') + + open('hardcode.py', 'w').write(''' +def process(filename): + js = open(filename).read() + replaced = js.replace("run();", "run(%s.concat(Module[\\"arguments\\"]));") + assert js != replaced + open(filename, 'w').write(replaced) +import sys +process(sys.argv[1]) +''' % str(args[:-1]) # do not hardcode in the last argument, the default arg +) + + try_delete(final_filename) + output = Popen([PYTHON, EMCC, filename, #'-O3', + '-O2', '-s', 'DOUBLE_MODE=0', '-s', 'PRECISE_I64_MATH=0', + '--llvm-lto', '3', '--memory-init-file', '0', '--js-transform', 'python hardcode.py', + '-s', 'TOTAL_MEMORY=128*1024*1024', + '--closure', '1', + #'-g', + '-o', final_filename] + shared_args + emcc_args, stdout=PIPE, stderr=self.stderr_redirect).communicate() + assert os.path.exists(final_filename), 'Failed to compile file: ' + output[0] + + # Run JS + global total_times, tests_done + times = [] + for i in range(reps): + start = time.time() + js_output = run_js(final_filename, engine=JS_ENGINE, args=args, stderr=PIPE, full_output=True) + + if i == 0 and 'uccessfully compiled asm.js code' in js_output: + if 'asm.js link error' not in js_output: + print "[%s was asm.js'ified]" % name + if not output_parser: + curr = time.time()-start + else: + curr = output_parser(js_output) + times.append(curr) + total_times[tests_done] += curr + if i == 0: + # Sanity check on output + self.assertContained(expected_output, js_output) + + # Run natively + if not native_exec: + self.build_native(filename, shared_args + native_args) + else: + shutil.copyfile(native_exec, filename + '.native') + shutil.copymode(native_exec, filename + '.native') + global total_native_times + native_times = [] + for i in range(reps): + start = time.time() + native_output = self.run_native(filename, args) + if i == 0: + # Sanity check on output + self.assertContained(expected_output, native_output) + if not output_parser: + curr = time.time()-start + else: + curr = output_parser(native_output) + native_times.append(curr) + total_native_times[tests_done] += curr + + self.print_stats(times, native_times, reps=reps) + + #tests_done += 1 + #if tests_done == TOTAL_TESTS: + # print 'Total stats:', + # self.print_stats(total_times, total_native_times, last=True) + + def test_primes(self): + src = r''' + #include + #include + int main(int argc, char **argv) { + int arg = argc > 1 ? argv[1][0] - '0' : 3; + switch(arg) { + case 0: return 0; break; + case 1: arg = 33000; break; + case 2: arg = 130000; break; + case 3: arg = 220000; break; + case 4: arg = 610000; break; + case 5: arg = 1010000; break; + default: printf("error: %d\\n", arg); return -1; + } + + int primes = 0, curri = 2; + while (primes < arg) { + int ok = true; + for (int j = 2; j < sqrtf(curri); j++) { + if (curri % j == 0) { + ok = false; + break; + } + } + if (ok) { + primes++; + } + curri++; + } + printf("lastprime: %d.\n", curri-1); + return 0; + } + ''' + self.do_benchmark('primes', src, 'lastprime:') + + def test_memops(self): + src = ''' + #include + #include + #include + int main(int argc, char **argv) { + int N, M; + int arg = argc > 1 ? argv[1][0] - '0' : 3; + switch(arg) { + case 0: return 0; break; + case 1: N = 1024*1024; M = 55; break; + case 2: N = 1024*1024; M = 400; break; + case 3: N = 1024*1024; M = 800; break; + case 4: N = 1024*1024; M = 4000; break; + case 5: N = 1024*1024; M = 8000; break; + default: printf("error: %d\\n", arg); return -1; + } + + int final = 0; + char *buf = (char*)malloc(N); + for (int t = 0; t < M; t++) { + for (int i = 0; i < N; i++) + buf[i] = (i + final)%256; + for (int i = 0; i < N; i++) + final += buf[i] & 1; + final = final % 1000; + } + printf("final: %d.\\n", final); + return 0; + } + ''' + self.do_benchmark('memops', src, 'final:') + + def zzztest_files(self): + src = r''' + #include + #include + #include + #include + + int main() { + int N = 100; + int M = 1000; + int K = 1000; + unsigned char *k = (unsigned char*)malloc(K+1), *k2 = (unsigned char*)malloc(K+1); + for (int i = 0; i < K; i++) { + k[i] = (i % 250) + 1; + } + k[K] = 0; + char buf[100]; + for (int i = 0; i < N; i++) { + sprintf(buf, "/dev/shm/file-%d.dat", i); + FILE *f = fopen(buf, "w"); + for (int j = 0; j < M; j++) { + fwrite(k, 1, (j % K) + 1, f); + } + fclose(f); + } + for (int i = 0; i < N; i++) { + sprintf(buf, "/dev/shm/file-%d.dat", i); + FILE *f = fopen(buf, "r"); + for (int j = 0; j < M; j++) { + fread(k2, 1, (j % K) + 1, f); + } + fclose(f); + for (int j = 0; j < K; j++) { + assert(k[j] == k2[j]); + } + unlink(buf); + } + printf("ok"); + return 0; + } + ''' + self.do_benchmark(src, 'ok') + + def test_copy(self): + src = r''' + #include + struct vec { + int x, y, z; + int r, g, b; + vec(int x_, int y_, int z_, int r_, int g_, int b_) : x(x_), y(y_), z(z_), r(r_), g(g_), b(b_) {} + static vec add(vec a, vec b) { + return vec(a.x+b.x, a.y+b.y, a.z+b.z, a.r+b.r, a.g+b.g, a.b+b.b); + } + void norm() { + x %= 1024; + y %= 1024; + z %= 1024; + r %= 1024; + b %= 1024; + g %= 1024; + } + int sum() { return x + y + z + r + g + b; } + }; + int main(int argc, char **argv) { + int arg = argc > 1 ? argv[1][0] - '0' : 3; + switch(arg) { + case 0: return 0; break; + case 1: arg = 75; break; + case 2: arg = 625; break; + case 3: arg = 1250; break; + case 4: arg = 5*1250; break; + case 5: arg = 10*1250; break; + default: printf("error: %d\\n", arg); return -1; + } + + int total = 0; + for (int i = 0; i < arg; i++) { + for (int j = 0; j < 50000; j++) { + vec c(i, i+i%10, j*2, i%255, j%120, i%15); + vec d(j+i%10, j*2, j%255, i%120, j%15, j); + vec e = c; + c.norm(); + d.norm(); + vec f = vec::add(c, d); + f = vec::add(e, f); + f.norm(); + f = vec::add(d, f); + total += f.sum() % 100; + total %= 10240; + } + } + printf("sum:%d\n", total); + return 0; + } + ''' + self.do_benchmark('copy', src, 'sum:') + + def test_fannkuch(self): + src = open(path_from_root('tests', 'fannkuch.cpp'), 'r').read().replace( + 'int n = argc > 1 ? atoi(argv[1]) : 0;', + ''' + int n; + int arg = argc > 1 ? argv[1][0] - '0' : 3; + switch(arg) { + case 0: return 0; break; + case 1: n = 9; break; + case 2: n = 10; break; + case 3: n = 11; break; + case 4: n = 11; break; + case 5: n = 12; break; + default: printf("error: %d\\n", arg); return -1; + } + ''' + ) + assert 'switch(arg)' in src + self.do_benchmark('fannkuch', src, 'Pfannkuchen(') + + def test_corrections(self): + src = r''' + #include + #include + int main(int argc, char **argv) { + int N, M; + int arg = argc > 1 ? argv[1][0] - '0' : 3; + switch(arg) { + case 0: return 0; break; + case 1: N = 20000; M = 550; break; + case 2: N = 20000; M = 3500; break; + case 3: N = 20000; M = 7000; break; + case 4: N = 20000; M = 5*7000; break; + case 5: N = 20000; M = 10*7000; break; + default: printf("error: %d\\n", arg); return -1; + } + + unsigned int f = 0; + unsigned short s = 0; + for (int t = 0; t < M; t++) { + for (int i = 0; i < N; i++) { + f += i / ((t % 5)+1); + if (f > 1000) f /= (t % 3)+1; + if (i % 4 == 0) f += i * (i % 8 == 0 ? 1 : -1); + s += (short(f)*short(f)) % 256; + } + } + printf("final: %d:%d.\n", f, s); + return 0; + } + ''' + self.do_benchmark('corrections', src, 'final:', emcc_args=['-s', 'CORRECT_SIGNS=1', '-s', 'CORRECT_OVERFLOWS=1', '-s', 'CORRECT_ROUNDINGS=1']) + + def fasta(self, name, double_rep, emcc_args=[]): + src = open(path_from_root('tests', 'fasta.cpp'), 'r').read().replace('double', double_rep) + src = src.replace(' const size_t n = ( argc > 1 ) ? atoi( argv[1] ) : 512;', ''' + int n; + int arg = argc > 1 ? argv[1][0] - '0' : 3; + switch(arg) { + case 0: return 0; break; + case 1: n = 19000000/20; break; + case 2: n = 19000000/2; break; + case 3: n = 19000000; break; + case 4: n = 19000000*5; break; + case 5: n = 19000000*10; break; + default: printf("error: %d\\n", arg); return -1; + } + ''') + assert 'switch(arg)' in src + self.do_benchmark('fasta', src, '') + + def test_fasta_float(self): + self.fasta('fasta_float', 'float') + + def test_fasta_double(self): + self.fasta('fasta_double', 'double') + + def test_fasta_double_full(self): + self.fasta('fasta_double_full', 'double', emcc_args=['-s', 'DOUBLE_MODE=1']) + + def test_skinning(self): + src = open(path_from_root('tests', 'skinning_test_no_simd.cpp'), 'r').read() + self.do_benchmark('skinning', src, 'blah=0.000000') + + def test_life(self): + src = open(path_from_root('tests', 'life.c'), 'r').read() + self.do_benchmark('life', src, '''--------------------------------''', shared_args=['-std=c99'], force_c=True) + + def test_linpack(self): + def output_parser(output): + return 100.0/float(re.search('Unrolled Double Precision +([\d\.]+) Mflops', output).group(1)) + self.do_benchmark('linpack', open(path_from_root('tests', 'linpack.c')).read(), '''Unrolled Double Precision''', force_c=True, output_parser=output_parser) + + def test_zzz_java_nbody(self): # tests xmlvm compiled java, including bitcasts of doubles, i64 math, etc. + args = [path_from_root('tests', 'nbody-java', x) for x in os.listdir(path_from_root('tests', 'nbody-java')) if x.endswith('.c')] + \ + ['-I' + path_from_root('tests', 'nbody-java')] + self.do_benchmark('nbody_java', '', '''Time(s)''', + force_c=True, emcc_args=args + ['-s', 'PRECISE_I64_MATH=1', '--llvm-lto', '2'], native_args=args + ['-lgc', '-std=c99', '-target', 'x86_64-pc-linux-gnu', '-lm']) + + def lua(self, benchmark, expected, output_parser=None, args_processor=None): + shutil.copyfile(path_from_root('tests', 'lua', benchmark + '.lua'), benchmark + '.lua') + #shutil.copyfile(path_from_root('tests', 'lua', 'binarytrees.lua'), 'binarytrees.lua') + #shutil.copyfile(path_from_root('tests', 'lua', 'scimark.lua'), 'scimark.lua') + emcc_args = self.get_library('lua', [os.path.join('src', 'lua'), os.path.join('src', 'liblua.a')], make=['make', 'generic'], configure=None) + \ + ['--embed-file', benchmark + '.lua'] + #['--embed-file', 'binarytrees.lua', '--embed-file', 'scimark.lua'] + ['--minify', '0'] + shutil.copyfile(emcc_args[0], emcc_args[0] + '.bc') + emcc_args[0] += '.bc' + native_args = self.get_library('lua_native', [os.path.join('src', 'lua'), os.path.join('src', 'liblua.a')], make=['make', 'generic'], configure=None, native=True) + + self.do_benchmark('lua_' + benchmark, '', expected, + force_c=True, args=[benchmark + '.lua', DEFAULT_ARG], emcc_args=emcc_args, native_args=native_args, native_exec=os.path.join('building', 'lua_native', 'src', 'lua'), + output_parser=output_parser, args_processor=args_processor) + + def test_zzz_lua_scimark(self): + def output_parser(output): + return 100.0/float(re.search('\nSciMark +([\d\.]+) ', output).group(1)) + + self.lua('scimark', '[small problem sizes]', output_parser=output_parser) + + def test_zzz_lua_binarytrees(self): + # js version: ['binarytrees.lua', {0: 0, 1: 9.5, 2: 11.99, 3: 12.85, 4: 14.72, 5: 15.82}[arguments[0]]] + self.lua('binarytrees', 'long lived tree of depth') + + def test_zzz_zlib(self): + src = open(path_from_root('tests', 'zlib', 'benchmark.c'), 'r').read() + emcc_args = self.get_library('zlib', os.path.join('libz.a'), make_args=['libz.a']) + \ + ['-I' + path_from_root('tests', 'zlib')] + native_args = self.get_library('zlib_native', os.path.join('libz.a'), make_args=['libz.a'], native=True) + \ + ['-I' + path_from_root('tests', 'zlib')] + self.do_benchmark('zlib', src, '''ok.''', + force_c=True, emcc_args=emcc_args, native_args=native_args) + + def test_zzz_box2d(self): # Called thus so it runs late in the alphabetical cycle... it is long + src = open(path_from_root('tests', 'box2d', 'Benchmark.cpp'), 'r').read() + + js_lib = self.get_library('box2d', [os.path.join('box2d.a')], configure=None) + native_lib = self.get_library('box2d_native', [os.path.join('box2d.a')], configure=None, native=True) + + emcc_args = js_lib + ['-I' + path_from_root('tests', 'box2d')] + native_args = native_lib + ['-I' + path_from_root('tests', 'box2d')] + + self.do_benchmark('box2d', src, 'frame averages', emcc_args=emcc_args, native_args=native_args) + + def test_zzz_bullet(self): # Called thus so it runs late in the alphabetical cycle... it is long + src = open(path_from_root('tests', 'bullet', 'Demos', 'Benchmarks', 'BenchmarkDemo.cpp'), 'r').read() + \ + open(path_from_root('tests', 'bullet', 'Demos', 'Benchmarks', 'main.cpp'), 'r').read() + + js_lib = self.get_library('bullet', [os.path.join('src', '.libs', 'libBulletDynamics.a'), + os.path.join('src', '.libs', 'libBulletCollision.a'), + os.path.join('src', '.libs', 'libLinearMath.a')], + configure_args=['--disable-demos','--disable-dependency-tracking']) + native_lib = self.get_library('bullet_native', [os.path.join('src', '.libs', 'libBulletDynamics.a'), + os.path.join('src', '.libs', 'libBulletCollision.a'), + os.path.join('src', '.libs', 'libLinearMath.a')], + configure_args=['--disable-demos','--disable-dependency-tracking'], + native=True) + + emcc_args = js_lib + ['-I' + path_from_root('tests', 'bullet', 'src'), + '-I' + path_from_root('tests', 'bullet', 'Demos', 'Benchmarks'), + '-s', 'DEAD_FUNCTIONS=["__ZSt9terminatev"]'] + native_args = native_lib + ['-I' + path_from_root('tests', 'bullet', 'src'), + '-I' + path_from_root('tests', 'bullet', 'Demos', 'Benchmarks')] + + self.do_benchmark('bullet', src, '\nok.\n', emcc_args=emcc_args, native_args=native_args) \ No newline at end of file diff --git a/tests/test_browser.py b/tests/test_browser.py new file mode 100644 index 0000000000000..a24a2de4bc725 --- /dev/null +++ b/tests/test_browser.py @@ -0,0 +1,1365 @@ +import BaseHTTPServer, multiprocessing, os, shutil, subprocess, unittest +from runner import BrowserCore, path_from_root +from tools.shared import * + +''' Enable this code to run in another browser than webbrowser detects as default +def run_in_other_browser(url): + execute(['yourbrowser', url]) +webbrowser.open_new = run_in_other_browser +''' + +class browser(BrowserCore): + @staticmethod + def audio(): + print + print 'Running the browser audio tests. Make sure to listen to hear the correct results!' + print + audio_test_cases = [ + 'test_sdl_audio', + 'test_sdl_audio_mix_channels', + 'test_sdl_audio_mix', + 'test_sdl_audio_quickload', + 'test_openal_playback', + 'test_openal_buffers', + 'test_freealut' + ] + return unittest.TestSuite(map(browser, audio_test_cases)) + + @classmethod + def setUpClass(self): + super(browser, self).setUpClass() + print + print 'Running the browser tests. Make sure the browser allows popups from localhost.' + print + + def test_html(self): + # test HTML generation. + self.btest('hello_world_sdl.cpp', reference='htmltest.png', + message='You should see "hello, world!" and a colored cube.') + + def test_html_source_map(self): + if 'test_html_source_map' not in str(sys.argv): return self.skip('''This test +requires manual intervention; will not be run unless explicitly requested''') + cpp_file = os.path.join(self.get_dir(), 'src.cpp') + html_file = os.path.join(self.get_dir(), 'src.html') + # browsers will try to 'guess' the corresponding original line if a + # generated line is unmapped, so if we want to make sure that our + # numbering is correct, we need to provide a couple of 'possible wrong + # answers'. thus, we add some printf calls so that the cpp file gets + # multiple mapped lines. in other words, if the program consists of a + # single 'throw' statement, browsers may just map any thrown exception to + # that line, because it will be the only mapped line. + with open(cpp_file, 'w') as f: + f.write(r''' + #include + + int main() { + printf("Starting test\n"); + try { + throw 42; // line 8 + } catch (int e) { } + printf("done\n"); + return 0; + } + ''') + # use relative paths when calling emcc, because file:// URIs can only load + # sourceContent when the maps are relative paths + Popen([PYTHON, EMCC, 'src.cpp', '-o', 'src.html', '-g4'], + cwd=self.get_dir()).communicate() + webbrowser.open_new('file://' + html_file) + print ''' +Set the debugger to pause on exceptions +You should see an exception thrown at src.cpp:7. +Press any key to continue.''' + raw_input() + + def build_native_lzma(self): + lzma_native = path_from_root('third_party', 'lzma.js', 'lzma-native') + if os.path.isfile(lzma_native) and os.access(lzma_native, os.X_OK): return + + cwd = os.getcwd() + try: + os.chdir(path_from_root('third_party', 'lzma.js')) + Popen(['sh', './doit.sh']).communicate() + finally: + os.chdir(cwd) + + def test_split(self): + # test HTML generation. + self.reftest(path_from_root('tests', 'htmltest.png')) + output = Popen([PYTHON, EMCC, path_from_root('tests', 'hello_world_sdl.cpp'), '-o', 'something.js', '--split', '100', '--pre-js', 'reftest.js']).communicate() + assert os.path.exists(os.path.join(self.get_dir(), 'something.js')), 'must be main js file' + assert os.path.exists(os.path.join(self.get_dir(), 'something_functions.js')), 'must be functions js file' + assert os.path.exists(os.path.join(self.get_dir(), 'something.include.html')), 'must be js include file' + + open(os.path.join(self.get_dir(), 'something.html'), 'w').write(''' + + + + + + + Emscripten-Generated Code + + + +
+
Downloading...
+
+ +
+ +
+
+
+ +
+ ''' + open(os.path.join(self.get_dir(), 'something.include.html')).read() + ''' + + + ''') + + self.run_browser('something.html', 'You should see "hello, world!" and a colored cube.', '/report_result?0') + + def test_split_in_source_filenames(self): + self.reftest(path_from_root('tests', 'htmltest.png')) + output = Popen([PYTHON, EMCC, path_from_root('tests', 'hello_world_sdl.cpp'), '-o', 'something.js', '-g', '--split', '100', '--pre-js', 'reftest.js']).communicate() + assert os.path.exists(os.path.join(self.get_dir(), 'something.js')), 'must be main js file' + assert os.path.exists(self.get_dir() + '/something/' + path_from_root('tests', 'hello_world_sdl.cpp.js')), 'must be functions js file' + assert os.path.exists(os.path.join(self.get_dir(), 'something.include.html')), 'must be js include file' + + open(os.path.join(self.get_dir(), 'something.html'), 'w').write(''' + + + + + + + Emscripten-Generated Code + + + +
+
Downloading...
+
+ +
+ +
+
+
+ +
+ ''' + open(os.path.join(self.get_dir(), 'something.include.html')).read() + ''' + + + ''') + + self.run_browser('something.html', 'You should see "hello, world!" and a colored cube.', '/report_result?0') + + def test_compression(self): + open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write(self.with_report_result(r''' + #include + #include + int main() { + printf("hello compressed world\n"); + int result = 1; + REPORT_RESULT(); + return 0; + } + ''')) + + self.build_native_lzma() + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '-o', 'page.html', + '--compression', '%s,%s,%s' % (path_from_root('third_party', 'lzma.js', 'lzma-native'), + path_from_root('third_party', 'lzma.js', 'lzma-decoder.js'), + 'LZMA.decompress')]).communicate() + assert os.path.exists(os.path.join(self.get_dir(), 'page.js')), 'must be side js' + assert os.path.exists(os.path.join(self.get_dir(), 'page.js.compress')), 'must be side compressed js' + assert os.stat(os.path.join(self.get_dir(), 'page.js')).st_size > os.stat(os.path.join(self.get_dir(), 'page.js.compress')).st_size, 'compressed file must be smaller' + shutil.move(os.path.join(self.get_dir(), 'page.js'), 'page.js.renamedsoitcannotbefound'); + self.run_browser('page.html', '', '/report_result?1') + + def test_preload_file(self): + absolute_src_path = os.path.join(self.get_dir(), 'somefile.txt').replace('\\', '/') + open(absolute_src_path, 'w').write('''load me right before running the code please''') + + absolute_src_path2 = os.path.join(self.get_dir(), '.somefile.txt').replace('\\', '/') + open(absolute_src_path2, 'w').write('''load me right before running the code please''') + + def make_main(path): + print 'make main at', path + open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write(self.with_report_result(r''' + #include + #include + #include + int main() { + FILE *f = fopen("%s", "r"); + char buf[100]; + fread(buf, 1, 20, f); + buf[20] = 0; + fclose(f); + printf("|%%s|\n", buf); + + int result = !strcmp("load me right before", buf); + REPORT_RESULT(); + return 0; + } + ''' % path)) + + test_cases = [ + # (source preload-file string, file on target FS to load) + ("somefile.txt", "somefile.txt"), + (".somefile.txt@somefile.txt", "somefile.txt"), + ("./somefile.txt", "somefile.txt"), + ("somefile.txt@file.txt", "file.txt"), + ("./somefile.txt@file.txt", "file.txt"), + ("./somefile.txt@./file.txt", "file.txt"), + ("somefile.txt@/file.txt", "file.txt"), + ("somefile.txt@/", "somefile.txt"), + (absolute_src_path + "@file.txt", "file.txt"), + (absolute_src_path + "@/file.txt", "file.txt"), + (absolute_src_path + "@/", "somefile.txt"), + ("somefile.txt@/directory/file.txt", "/directory/file.txt"), + ("somefile.txt@/directory/file.txt", "directory/file.txt"), + (absolute_src_path + "@/directory/file.txt", "directory/file.txt")] + + for test in test_cases: + (srcpath, dstpath) = test + print 'Testing', srcpath, dstpath + make_main(dstpath) + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--preload-file', srcpath, '-o', 'page.html']).communicate() + self.run_browser('page.html', 'You should see |load me right before|.', '/report_result?1') + + # By absolute path + + make_main('somefile.txt') # absolute becomes relative + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--preload-file', absolute_src_path, '-o', 'page.html']).communicate() + self.run_browser('page.html', 'You should see |load me right before|.', '/report_result?1') + + # Test subdirectory handling with asset packaging. + os.makedirs(os.path.join(self.get_dir(), 'assets/sub/asset1/').replace('\\', '/')) + os.makedirs(os.path.join(self.get_dir(), 'assets/sub/asset1/.git').replace('\\', '/')) # Test adding directory that shouldn't exist. + os.makedirs(os.path.join(self.get_dir(), 'assets/sub/asset2/').replace('\\', '/')) + open(os.path.join(self.get_dir(), 'assets/sub/asset1/file1.txt'), 'w').write('''load me right before running the code please''') + open(os.path.join(self.get_dir(), 'assets/sub/asset1/.git/shouldnt_be_embedded.txt'), 'w').write('''this file should not get embedded''') + open(os.path.join(self.get_dir(), 'assets/sub/asset2/file2.txt'), 'w').write('''load me right before running the code please''') + absolute_assets_src_path = os.path.join(self.get_dir(), 'assets').replace('\\', '/') + def make_main_two_files(path1, path2, nonexistingpath): + open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write(self.with_report_result(r''' + #include + #include + #include + int main() { + FILE *f = fopen("%s", "r"); + char buf[100]; + fread(buf, 1, 20, f); + buf[20] = 0; + fclose(f); + printf("|%%s|\n", buf); + + int result = !strcmp("load me right before", buf); + + f = fopen("%s", "r"); + if (f == NULL) + result = 0; + fclose(f); + + f = fopen("%s", "r"); + if (f != NULL) + result = 0; + + REPORT_RESULT(); + return 0; + } + ''' % (path1, path2, nonexistingpath))) + + test_cases = [ + # (source directory to embed, file1 on target FS to load, file2 on target FS to load, name of a file that *shouldn't* exist on VFS) + ("assets", "assets/sub/asset1/file1.txt", "assets/sub/asset2/file2.txt", "assets/sub/asset1/.git/shouldnt_be_embedded.txt"), + ("assets/", "assets/sub/asset1/file1.txt", "assets/sub/asset2/file2.txt", "assets/sub/asset1/.git/shouldnt_be_embedded.txt"), + ("assets@/", "/sub/asset1/file1.txt", "/sub/asset2/file2.txt", "/sub/asset1/.git/shouldnt_be_embedded.txt"), + ("assets/@/", "/sub/asset1/file1.txt", "/sub/asset2/file2.txt", "/sub/asset1/.git/shouldnt_be_embedded.txt"), + ("assets@./", "/sub/asset1/file1.txt", "/sub/asset2/file2.txt", "/sub/asset1/.git/shouldnt_be_embedded.txt"), + (absolute_assets_src_path + "@/", "/sub/asset1/file1.txt", "/sub/asset2/file2.txt", "/sub/asset1/.git/shouldnt_be_embedded.txt"), + (absolute_assets_src_path + "@/assets", "/assets/sub/asset1/file1.txt", "/assets/sub/asset2/file2.txt", "assets/sub/asset1/.git/shouldnt_be_embedded.txt")] + + for test in test_cases: + (srcpath, dstpath1, dstpath2, nonexistingpath) = test + make_main_two_files(dstpath1, dstpath2, nonexistingpath) + print srcpath + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--preload-file', srcpath, '-o', 'page.html']).communicate() + self.run_browser('page.html', 'You should see |load me right before|.', '/report_result?1') + + # Should still work with -o subdir/.. + + make_main('somefile.txt') # absolute becomes relative + try: + os.mkdir(os.path.join(self.get_dir(), 'dirrey')) + except: + pass + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--preload-file', absolute_src_path, '-o', 'dirrey/page.html']).communicate() + self.run_browser('dirrey/page.html', 'You should see |load me right before|.', '/report_result?1') + + # With FS.preloadFile + + open(os.path.join(self.get_dir(), 'pre.js'), 'w').write(''' + Module.preRun = function() { + FS.createPreloadedFile('/', 'someotherfile.txt', 'somefile.txt', true, false); + }; + ''') + make_main('someotherfile.txt') + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--pre-js', 'pre.js', '-o', 'page.html']).communicate() + self.run_browser('page.html', 'You should see |load me right before|.', '/report_result?1') + + def test_preload_caching(self): + open(os.path.join(self.get_dir(), 'somefile.txt'), 'w').write('''load me right before running the code please''') + def make_main(path): + print path + open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write(self.with_report_result(r''' + #include + #include + #include + + extern "C" { + extern int checkPreloadResults(); + } + + int main(int argc, char** argv) { + FILE *f = fopen("%s", "r"); + char buf[100]; + fread(buf, 1, 20, f); + buf[20] = 0; + fclose(f); + printf("|%%s|\n", buf); + + int result = 0; + + result += !strcmp("load me right before", buf); + result += checkPreloadResults(); + + REPORT_RESULT(); + return 0; + } + ''' % path)) + + open(os.path.join(self.get_dir(), 'test.js'), 'w').write(''' + mergeInto(LibraryManager.library, { + checkPreloadResults: function() { + var cached = 0; + var packages = Object.keys(Module['preloadResults']); + packages.forEach(function(package) { + var fromCache = Module['preloadResults'][package]['fromCache']; + if (fromCache) + ++ cached; + }); + return cached; + } + }); + ''') + + make_main('somefile.txt') + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--use-preload-cache', '--js-library', os.path.join(self.get_dir(), 'test.js'), '--preload-file', 'somefile.txt', '-o', 'page.html']).communicate() + self.run_browser('page.html', 'You should see |load me right before|.', '/report_result?1') + self.run_browser('page.html', 'You should see |load me right before|.', '/report_result?2') + + def test_multifile(self): + # a few files inside a directory + self.clear() + os.makedirs(os.path.join(self.get_dir(), 'subdirr')); + os.makedirs(os.path.join(self.get_dir(), 'subdirr', 'moar')); + open(os.path.join(self.get_dir(), 'subdirr', 'data1.txt'), 'w').write('''1214141516171819''') + open(os.path.join(self.get_dir(), 'subdirr', 'moar', 'data2.txt'), 'w').write('''3.14159265358979''') + open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write(self.with_report_result(r''' + #include + #include + #include + int main() { + char buf[17]; + + FILE *f = fopen("subdirr/data1.txt", "r"); + fread(buf, 1, 16, f); + buf[16] = 0; + fclose(f); + printf("|%s|\n", buf); + int result = !strcmp("1214141516171819", buf); + + FILE *f2 = fopen("subdirr/moar/data2.txt", "r"); + fread(buf, 1, 16, f2); + buf[16] = 0; + fclose(f2); + printf("|%s|\n", buf); + result = result && !strcmp("3.14159265358979", buf); + + REPORT_RESULT(); + return 0; + } + ''')) + + # by individual files + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--preload-file', 'subdirr/data1.txt', '--preload-file', 'subdirr/moar/data2.txt', '-o', 'page.html']).communicate() + self.run_browser('page.html', 'You should see two cool numbers', '/report_result?1') + os.remove('page.html') + + # by directory, and remove files to make sure + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--preload-file', 'subdirr', '-o', 'page.html']).communicate() + shutil.rmtree(os.path.join(self.get_dir(), 'subdirr')) + self.run_browser('page.html', 'You should see two cool numbers', '/report_result?1') + + def test_compressed_file(self): + open(os.path.join(self.get_dir(), 'datafile.txt'), 'w').write('compress this please' + (2000*'.')) + open(os.path.join(self.get_dir(), 'datafile2.txt'), 'w').write('moar' + (100*'!')) + open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write(self.with_report_result(r''' + #include + #include + #include + int main() { + char buf[21]; + FILE *f = fopen("datafile.txt", "r"); + fread(buf, 1, 20, f); + buf[20] = 0; + fclose(f); + printf("file says: |%s|\n", buf); + int result = !strcmp("compress this please", buf); + FILE *f2 = fopen("datafile2.txt", "r"); + fread(buf, 1, 5, f2); + buf[5] = 0; + fclose(f2); + result = result && !strcmp("moar!", buf); + printf("file 2 says: |%s|\n", buf); + REPORT_RESULT(); + return 0; + } + ''')) + + self.build_native_lzma() + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '-o', 'page.html', '--preload-file', 'datafile.txt', '--preload-file', 'datafile2.txt', + '--compression', '%s,%s,%s' % (path_from_root('third_party', 'lzma.js', 'lzma-native'), + path_from_root('third_party', 'lzma.js', 'lzma-decoder.js'), + 'LZMA.decompress')]).communicate() + assert os.path.exists(os.path.join(self.get_dir(), 'datafile.txt')), 'must be data file' + assert os.path.exists(os.path.join(self.get_dir(), 'page.data.compress')), 'must be data file in compressed form' + assert os.stat(os.path.join(self.get_dir(), 'page.js')).st_size != os.stat(os.path.join(self.get_dir(), 'page.js.compress')).st_size, 'compressed file must be different' + shutil.move(os.path.join(self.get_dir(), 'datafile.txt'), 'datafile.txt.renamedsoitcannotbefound'); + self.run_browser('page.html', '', '/report_result?1') + + def test_sdl_image(self): + # load an image file, get pixel data. Also O2 coverage for --preload-file, and memory-init + shutil.copyfile(path_from_root('tests', 'screenshot.jpg'), os.path.join(self.get_dir(), 'screenshot.jpg')) + open(os.path.join(self.get_dir(), 'sdl_image.c'), 'w').write(self.with_report_result(open(path_from_root('tests', 'sdl_image.c')).read())) + + for mem in [0, 1]: + for dest, dirname, basename in [('screenshot.jpg', '/', 'screenshot.jpg'), + ('screenshot.jpg@/assets/screenshot.jpg', '/assets', 'screenshot.jpg')]: + Popen([ + PYTHON, EMCC, os.path.join(self.get_dir(), 'sdl_image.c'), '-o', 'page.html', '-O2', '--memory-init-file', str(mem), + '--preload-file', dest, '-DSCREENSHOT_DIRNAME="' + dirname + '"', '-DSCREENSHOT_BASENAME="' + basename + '"' + ]).communicate() + self.run_browser('page.html', '', '/report_result?600') + + def test_sdl_image_jpeg(self): + shutil.copyfile(path_from_root('tests', 'screenshot.jpg'), os.path.join(self.get_dir(), 'screenshot.jpeg')) + open(os.path.join(self.get_dir(), 'sdl_image_jpeg.c'), 'w').write(self.with_report_result(open(path_from_root('tests', 'sdl_image.c')).read())) + Popen([ + PYTHON, EMCC, os.path.join(self.get_dir(), 'sdl_image_jpeg.c'), '-o', 'page.html', + '--preload-file', 'screenshot.jpeg', '-DSCREENSHOT_DIRNAME="/"', '-DSCREENSHOT_BASENAME="screenshot.jpeg"' + ]).communicate() + self.run_browser('page.html', '', '/report_result?600') + + def test_sdl_image_compressed(self): + for image, width in [(path_from_root('tests', 'screenshot2.png'), 300), + (path_from_root('tests', 'screenshot.jpg'), 600)]: + self.clear() + print image + + basename = os.path.basename(image) + shutil.copyfile(image, os.path.join(self.get_dir(), basename)) + open(os.path.join(self.get_dir(), 'sdl_image.c'), 'w').write(self.with_report_result(open(path_from_root('tests', 'sdl_image.c')).read())) + + self.build_native_lzma() + Popen([ + PYTHON, EMCC, os.path.join(self.get_dir(), 'sdl_image.c'), '-o', 'page.html', + '--preload-file', basename, '-DSCREENSHOT_DIRNAME="/"', '-DSCREENSHOT_BASENAME="' + basename + '"', + '--compression', '%s,%s,%s' % (path_from_root('third_party', 'lzma.js', 'lzma-native'), + path_from_root('third_party', 'lzma.js', 'lzma-decoder.js'), + 'LZMA.decompress') + ]).communicate() + shutil.move(os.path.join(self.get_dir(), basename), basename + '.renamedsoitcannotbefound'); + self.run_browser('page.html', '', '/report_result?' + str(width)) + + def test_sdl_image_prepare(self): + # load an image file, get pixel data. + shutil.copyfile(path_from_root('tests', 'screenshot.jpg'), os.path.join(self.get_dir(), 'screenshot.not')) + self.btest('sdl_image_prepare.c', reference='screenshot.jpg', args=['--preload-file', 'screenshot.not']) + + def test_sdl_image_prepare_data(self): + # load an image file, get pixel data. + shutil.copyfile(path_from_root('tests', 'screenshot.jpg'), os.path.join(self.get_dir(), 'screenshot.not')) + self.btest('sdl_image_prepare_data.c', reference='screenshot.jpg', args=['--preload-file', 'screenshot.not']) + + def test_sdl_stb_image(self): + # load an image file, get pixel data. + shutil.copyfile(path_from_root('tests', 'screenshot.jpg'), os.path.join(self.get_dir(), 'screenshot.not')) + self.btest('sdl_stb_image.c', reference='screenshot.jpg', args=['-s', 'STB_IMAGE=1', '--preload-file', 'screenshot.not']) + + def test_sdl_stb_image_data(self): + # load an image file, get pixel data. + shutil.copyfile(path_from_root('tests', 'screenshot.jpg'), os.path.join(self.get_dir(), 'screenshot.not')) + self.btest('sdl_stb_image_data.c', reference='screenshot.jpg', args=['-s', 'STB_IMAGE=1', '--preload-file', 'screenshot.not']) + + def test_sdl_canvas(self): + open(os.path.join(self.get_dir(), 'sdl_canvas.c'), 'w').write(self.with_report_result(open(path_from_root('tests', 'sdl_canvas.c')).read())) + + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'sdl_canvas.c'), '-o', 'page.html']).communicate() + self.run_browser('page.html', '', '/report_result?1') + + def test_sdl_key(self): + open(os.path.join(self.get_dir(), 'pre.js'), 'w').write(''' + Module.postRun = function() { + function doOne() { + Module._one(); + setTimeout(doOne, 1000/60); + } + setTimeout(doOne, 1000/60); + } + + function keydown(c) { + var event = document.createEvent("KeyboardEvent"); + event.initKeyEvent("keydown", true, true, window, + 0, 0, 0, 0, + c, c); + document.dispatchEvent(event); + } + + function keyup(c) { + var event = document.createEvent("KeyboardEvent"); + event.initKeyEvent("keyup", true, true, window, + 0, 0, 0, 0, + c, c); + document.dispatchEvent(event); + } + ''') + open(os.path.join(self.get_dir(), 'sdl_key.c'), 'w').write(self.with_report_result(open(path_from_root('tests', 'sdl_key.c')).read())) + + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'sdl_key.c'), '-o', 'page.html', '--pre-js', 'pre.js', '-s', '''EXPORTED_FUNCTIONS=['_main', '_one']''']).communicate() + self.run_browser('page.html', '', '/report_result?223092870') + + def test_sdl_text(self): + open(os.path.join(self.get_dir(), 'pre.js'), 'w').write(''' + Module.postRun = function() { + function doOne() { + Module._one(); + setTimeout(doOne, 1000/60); + } + setTimeout(doOne, 1000/60); + } + + function simulateKeyEvent(charCode) { + var event = document.createEvent("KeyboardEvent"); + event.initKeyEvent("keypress", true, true, window, + 0, 0, 0, 0, 0, charCode); + document.body.dispatchEvent(event); + } + ''') + open(os.path.join(self.get_dir(), 'sdl_text.c'), 'w').write(self.with_report_result(open(path_from_root('tests', 'sdl_text.c')).read())) + + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'sdl_text.c'), '-o', 'page.html', '--pre-js', 'pre.js', '-s', '''EXPORTED_FUNCTIONS=['_main', '_one']''']).communicate() + self.run_browser('page.html', '', '/report_result?1') + + def test_sdl_mouse(self): + open(os.path.join(self.get_dir(), 'pre.js'), 'w').write(''' + function simulateMouseEvent(x, y, button) { + var event = document.createEvent("MouseEvents"); + if (button >= 0) { + var event1 = document.createEvent("MouseEvents"); + event1.initMouseEvent('mousedown', true, true, window, + 1, Module['canvas'].offsetLeft + x, Module['canvas'].offsetTop + y, Module['canvas'].offsetLeft + x, Module['canvas'].offsetTop + y, + 0, 0, 0, 0, + button, null); + Module['canvas'].dispatchEvent(event1); + var event2 = document.createEvent("MouseEvents"); + event2.initMouseEvent('mouseup', true, true, window, + 1, Module['canvas'].offsetLeft + x, Module['canvas'].offsetTop + y, Module['canvas'].offsetLeft + x, Module['canvas'].offsetTop + y, + 0, 0, 0, 0, + button, null); + Module['canvas'].dispatchEvent(event2); + } else { + var event1 = document.createEvent("MouseEvents"); + event1.initMouseEvent('mousemove', true, true, window, + 0, Module['canvas'].offsetLeft + x, Module['canvas'].offsetTop + y, Module['canvas'].offsetLeft + x, Module['canvas'].offsetTop + y, + 0, 0, 0, 0, + 0, null); + Module['canvas'].dispatchEvent(event1); + } + } + window['simulateMouseEvent'] = simulateMouseEvent; + ''') + open(os.path.join(self.get_dir(), 'sdl_mouse.c'), 'w').write(self.with_report_result(open(path_from_root('tests', 'sdl_mouse.c')).read())) + + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'sdl_mouse.c'), '-O2', '--minify', '0', '-o', 'page.html', '--pre-js', 'pre.js']).communicate() + self.run_browser('page.html', '', '/report_result?740') + + def test_sdl_mouse_offsets(self): + open(os.path.join(self.get_dir(), 'pre.js'), 'w').write(''' + function simulateMouseEvent(x, y, button) { + var event = document.createEvent("MouseEvents"); + if (button >= 0) { + var event1 = document.createEvent("MouseEvents"); + event1.initMouseEvent('mousedown', true, true, window, + 1, x, y, x, y, + 0, 0, 0, 0, + button, null); + Module['canvas'].dispatchEvent(event1); + var event2 = document.createEvent("MouseEvents"); + event2.initMouseEvent('mouseup', true, true, window, + 1, x, y, x, y, + 0, 0, 0, 0, + button, null); + Module['canvas'].dispatchEvent(event2); + } else { + var event1 = document.createEvent("MouseEvents"); + event1.initMouseEvent('mousemove', true, true, window, + 0, x, y, x, y, + 0, 0, 0, 0, + 0, null); + Module['canvas'].dispatchEvent(event1); + } + } + window['simulateMouseEvent'] = simulateMouseEvent; + ''') + open(os.path.join(self.get_dir(), 'page.html'), 'w').write(''' + + + + + +
+ +
+ + + + + + ''') + open(os.path.join(self.get_dir(), 'sdl_mouse.c'), 'w').write(self.with_report_result(open(path_from_root('tests', 'sdl_mouse.c')).read())) + + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'sdl_mouse.c'), '-O2', '--minify', '0', '-o', 'sdl_mouse.js', '--pre-js', 'pre.js']).communicate() + self.run_browser('page.html', '', '/report_result?600') + + def test_sdl_audio(self): + shutil.copyfile(path_from_root('tests', 'sounds', 'alarmvictory_1.ogg'), os.path.join(self.get_dir(), 'sound.ogg')) + shutil.copyfile(path_from_root('tests', 'sounds', 'alarmcreatemiltaryfoot_1.wav'), os.path.join(self.get_dir(), 'sound2.wav')) + shutil.copyfile(path_from_root('tests', 'sounds', 'noise.ogg'), os.path.join(self.get_dir(), 'noise.ogg')) + shutil.copyfile(path_from_root('tests', 'sounds', 'the_entertainer.ogg'), os.path.join(self.get_dir(), 'the_entertainer.ogg')) + open(os.path.join(self.get_dir(), 'bad.ogg'), 'w').write('I claim to be audio, but am lying') + open(os.path.join(self.get_dir(), 'sdl_audio.c'), 'w').write(self.with_report_result(open(path_from_root('tests', 'sdl_audio.c')).read())) + + # use closure to check for a possible bug with closure minifying away newer Audio() attributes + Popen([PYTHON, EMCC, '-O2', '--closure', '1', '--minify', '0', os.path.join(self.get_dir(), 'sdl_audio.c'), '--preload-file', 'sound.ogg', '--preload-file', 'sound2.wav', '--embed-file', 'the_entertainer.ogg', '--preload-file', 'noise.ogg', '--preload-file', 'bad.ogg', '-o', 'page.html', '-s', 'EXPORTED_FUNCTIONS=["_main", "_play", "_play2"]']).communicate() + self.run_browser('page.html', '', '/report_result?1') + + def test_sdl_audio_mix_channels(self): + shutil.copyfile(path_from_root('tests', 'sounds', 'noise.ogg'), os.path.join(self.get_dir(), 'sound.ogg')) + open(os.path.join(self.get_dir(), 'sdl_audio_mix_channels.c'), 'w').write(self.with_report_result(open(path_from_root('tests', 'sdl_audio_mix_channels.c')).read())) + + Popen([PYTHON, EMCC, '-O2', '--minify', '0', os.path.join(self.get_dir(), 'sdl_audio_mix_channels.c'), '--preload-file', 'sound.ogg', '-o', 'page.html']).communicate() + self.run_browser('page.html', '', '/report_result?1') + + def test_sdl_audio_mix(self): + shutil.copyfile(path_from_root('tests', 'sounds', 'pluck.ogg'), os.path.join(self.get_dir(), 'sound.ogg')) + shutil.copyfile(path_from_root('tests', 'sounds', 'the_entertainer.ogg'), os.path.join(self.get_dir(), 'music.ogg')) + shutil.copyfile(path_from_root('tests', 'sounds', 'noise.ogg'), os.path.join(self.get_dir(), 'noise.ogg')) + open(os.path.join(self.get_dir(), 'sdl_audio_mix.c'), 'w').write(self.with_report_result(open(path_from_root('tests', 'sdl_audio_mix.c')).read())) + + Popen([PYTHON, EMCC, '-O2', '--minify', '0', os.path.join(self.get_dir(), 'sdl_audio_mix.c'), '--preload-file', 'sound.ogg', '--preload-file', 'music.ogg', '--preload-file', 'noise.ogg', '-o', 'page.html']).communicate() + self.run_browser('page.html', '', '/report_result?1') + + def test_sdl_audio_quickload(self): + open(os.path.join(self.get_dir(), 'sdl_audio_quickload.c'), 'w').write(self.with_report_result(open(path_from_root('tests', 'sdl_audio_quickload.c')).read())) + + Popen([PYTHON, EMCC, '-O2', '--minify', '0', os.path.join(self.get_dir(), 'sdl_audio_quickload.c'), '-o', 'page.html', '-s', 'EXPORTED_FUNCTIONS=["_main", "_play"]']).communicate() + self.run_browser('page.html', '', '/report_result?1') + + def test_sdl_gl_read(self): + # SDL, OpenGL, readPixels + open(os.path.join(self.get_dir(), 'sdl_gl_read.c'), 'w').write(self.with_report_result(open(path_from_root('tests', 'sdl_gl_read.c')).read())) + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'sdl_gl_read.c'), '-o', 'something.html']).communicate() + self.run_browser('something.html', '.', '/report_result?1') + + def test_sdl_ogl(self): + shutil.copyfile(path_from_root('tests', 'screenshot.png'), os.path.join(self.get_dir(), 'screenshot.png')) + self.btest('sdl_ogl.c', reference='screenshot-gray-purple.png', reference_slack=1, + args=['-O2', '--minify', '0', '--preload-file', 'screenshot.png'], + message='You should see an image with gray at the top.') + + def test_sdl_ogl_defaultmatrixmode(self): + shutil.copyfile(path_from_root('tests', 'screenshot.png'), os.path.join(self.get_dir(), 'screenshot.png')) + self.btest('sdl_ogl_defaultMatrixMode.c', reference='screenshot-gray-purple.png', reference_slack=1, + args=['--minify', '0', '--preload-file', 'screenshot.png'], + message='You should see an image with gray at the top.') + + def test_sdl_ogl_p(self): + # Immediate mode with pointers + shutil.copyfile(path_from_root('tests', 'screenshot.png'), os.path.join(self.get_dir(), 'screenshot.png')) + self.btest('sdl_ogl_p.c', reference='screenshot-gray.png', reference_slack=1, + args=['--preload-file', 'screenshot.png'], + message='You should see an image with gray at the top.') + + def test_sdl_fog_simple(self): + shutil.copyfile(path_from_root('tests', 'screenshot.png'), os.path.join(self.get_dir(), 'screenshot.png')) + self.btest('sdl_fog_simple.c', reference='screenshot-fog-simple.png', + args=['-O2', '--minify', '0', '--preload-file', 'screenshot.png'], + message='You should see an image with fog.') + + def test_sdl_fog_negative(self): + shutil.copyfile(path_from_root('tests', 'screenshot.png'), os.path.join(self.get_dir(), 'screenshot.png')) + self.btest('sdl_fog_negative.c', reference='screenshot-fog-negative.png', + args=['--preload-file', 'screenshot.png'], + message='You should see an image with fog.') + + def test_sdl_fog_density(self): + shutil.copyfile(path_from_root('tests', 'screenshot.png'), os.path.join(self.get_dir(), 'screenshot.png')) + self.btest('sdl_fog_density.c', reference='screenshot-fog-density.png', + args=['--preload-file', 'screenshot.png'], + message='You should see an image with fog.') + + def test_sdl_fog_exp2(self): + shutil.copyfile(path_from_root('tests', 'screenshot.png'), os.path.join(self.get_dir(), 'screenshot.png')) + self.btest('sdl_fog_exp2.c', reference='screenshot-fog-exp2.png', + args=['--preload-file', 'screenshot.png'], + message='You should see an image with fog.') + + def test_sdl_fog_linear(self): + shutil.copyfile(path_from_root('tests', 'screenshot.png'), os.path.join(self.get_dir(), 'screenshot.png')) + self.btest('sdl_fog_linear.c', reference='screenshot-fog-linear.png', reference_slack=1, + args=['--preload-file', 'screenshot.png'], + message='You should see an image with fog.') + + def test_openal_playback(self): + shutil.copyfile(path_from_root('tests', 'sounds', 'audio.wav'), os.path.join(self.get_dir(), 'audio.wav')) + open(os.path.join(self.get_dir(), 'openal_playback.cpp'), 'w').write(self.with_report_result(open(path_from_root('tests', 'openal_playback.cpp')).read())) + + Popen([PYTHON, EMCC, '-O2', os.path.join(self.get_dir(), 'openal_playback.cpp'), '--preload-file', 'audio.wav', '-o', 'page.html']).communicate() + self.run_browser('page.html', '', '/report_result?1') + + def test_openal_buffers(self): + shutil.copyfile(path_from_root('tests', 'sounds', 'the_entertainer.wav'), os.path.join(self.get_dir(), 'the_entertainer.wav')) + self.btest('openal_buffers.c', '0', args=['--preload-file', 'the_entertainer.wav'],) + + def test_glfw(self): + open(os.path.join(self.get_dir(), 'glfw.c'), 'w').write(self.with_report_result(open(path_from_root('tests', 'glfw.c')).read())) + + Popen([PYTHON, EMCC, '-O2', os.path.join(self.get_dir(), 'glfw.c'), '-o', 'page.html']).communicate() + self.run_browser('page.html', '', '/report_result?1') + + def test_egl_width_height(self): + open(os.path.join(self.get_dir(), 'test_egl_width_height.c'), 'w').write(self.with_report_result(open(path_from_root('tests', 'test_egl_width_height.c')).read())) + + Popen([PYTHON, EMCC, '-O2', os.path.join(self.get_dir(), 'test_egl_width_height.c'), '-o', 'page.html']).communicate() + self.run_browser('page.html', 'Should print "(300, 150)" -- the size of the canvas in pixels', '/report_result?1') + + def test_freealut(self): + programs = self.get_library('freealut', os.path.join('examples', '.libs', 'hello_world.bc'), make_args=['EXEEXT=.bc']) + for program in programs: + assert os.path.exists(program) + Popen([PYTHON, EMCC, '-O2', program, '-o', 'page.html']).communicate() + self.run_browser('page.html', 'You should hear "Hello World!"') + + def test_worker(self): + # Test running in a web worker + output = Popen([PYTHON, EMCC, path_from_root('tests', 'hello_world_worker.cpp'), '-o', 'worker.js'], stdout=PIPE, stderr=PIPE).communicate() + assert len(output[0]) == 0, output[0] + assert os.path.exists('worker.js'), output + self.assertContained('you should not see this text when in a worker!', run_js('worker.js')) # code should run standalone + html_file = open('main.html', 'w') + html_file.write(''' + + + Worker Test + + + + ''') + html_file.close() + self.run_browser('main.html', 'You should see that the worker was called, and said "hello from worker!"', '/report_result?hello%20from%20worker!') + + def test_chunked_synchronous_xhr(self): + main = 'chunked_sync_xhr.html' + worker_filename = "download_and_checksum_worker.js" + + html_file = open(main, 'w') + html_file.write(r""" + + + Chunked XHR + + + Chunked XHR Web Worker Test + + + + """) + html_file.close() + + c_source_filename = "checksummer.c" + + prejs_filename = "worker_prejs.js" + prejs_file = open(prejs_filename, 'w') + prejs_file.write(r""" + if (typeof(Module) === "undefined") Module = {}; + Module["arguments"] = ["/bigfile"]; + Module["preInit"] = function() { + FS.createLazyFile('/', "bigfile", "http://localhost:11111/bogus_file_path", true, false); + }; + var doTrace = true; + Module["print"] = function(s) { self.postMessage({channel: "stdout", line: s}); }; + Module["stderr"] = function(s) { self.postMessage({channel: "stderr", char: s, trace: ((doTrace && s === 10) ? new Error().stack : null)}); doTrace = false; }; + """) + prejs_file.close() + # vs. os.path.join(self.get_dir(), filename) + # vs. path_from_root('tests', 'hello_world_gles.c') + Popen([PYTHON, EMCC, path_from_root('tests', c_source_filename), '-g', '-s', 'SMALL_CHUNKS=1', '-o', worker_filename, + '--pre-js', prejs_filename]).communicate() + + chunkSize = 1024 + data = os.urandom(10*chunkSize+1) # 10 full chunks and one 1 byte chunk + expectedConns = 11 + import zlib + checksum = zlib.adler32(data) + + def chunked_server(support_byte_ranges): + class ChunkedServerHandler(BaseHTTPServer.BaseHTTPRequestHandler): + def sendheaders(s, extra=[], length=len(data)): + s.send_response(200) + s.send_header("Content-Length", str(length)) + s.send_header("Access-Control-Allow-Origin", "http://localhost:8888") + s.send_header("Access-Control-Expose-Headers", "Content-Length, Accept-Ranges") + s.send_header("Content-type", "application/octet-stream") + if support_byte_ranges: + s.send_header("Accept-Ranges", "bytes") + for i in extra: + s.send_header(i[0], i[1]) + s.end_headers() + + def do_HEAD(s): + s.sendheaders() + + def do_OPTIONS(s): + s.sendheaders([("Access-Control-Allow-Headers", "Range")], 0) + + def do_GET(s): + if not support_byte_ranges: + s.sendheaders() + s.wfile.write(data) + else: + (start, end) = s.headers.get("range").split("=")[1].split("-") + start = int(start) + end = int(end) + end = min(len(data)-1, end) + length = end-start+1 + s.sendheaders([],length) + s.wfile.write(data[start:end+1]) + s.wfile.close() + httpd = BaseHTTPServer.HTTPServer(('localhost', 11111), ChunkedServerHandler) + for i in range(expectedConns+1): + httpd.handle_request() + + server = multiprocessing.Process(target=chunked_server, args=(True,)) + server.start() + self.run_browser(main, 'Chunked binary synchronous XHR in Web Workers!', '/report_result?' + str(checksum)) + server.terminate() + + def test_glgears(self): + self.btest('hello_world_gles.c', reference='gears.png', + args=['-DHAVE_BUILTIN_SINCOS'], outfile='something.html', + message='You should see animating gears.') + + def test_glgears_animation(self): + es2_suffix = ['', '_full', '_full_944'] + for full_es2 in [0, 1, 2]: + for emulation in [0, 1]: + if full_es2 and emulation: continue + print full_es2, emulation + Popen([PYTHON, EMCC, path_from_root('tests', 'hello_world_gles%s.c' % es2_suffix[full_es2]), '-o', 'something.html', + '-DHAVE_BUILTIN_SINCOS', '-s', 'GL_TESTING=1', + '--shell-file', path_from_root('tests', 'hello_world_gles_shell.html')] + + (['-s', 'FORCE_GL_EMULATION=1'] if emulation else []) + + (['-s', 'FULL_ES2=1'] if full_es2 else []), + ).communicate() + self.run_browser('something.html', 'You should see animating gears.', '/report_gl_result?true') + assert ('var GLEmulation' in open(self.in_dir('something.html')).read()) == emulation, "emulation code should be added when asked for" + + def test_fulles2_sdlproc(self): + self.btest('full_es2_sdlproc.c', '1', args=['-s', 'GL_TESTING=1', '-DHAVE_BUILTIN_SINCOS', '-s', 'FULL_ES2=1']) + + def test_glgears_deriv(self): + self.btest('hello_world_gles_deriv.c', reference='gears.png', + args=['-DHAVE_BUILTIN_SINCOS'], outfile='something.html', + message='You should see animating gears.') + with open('something.html') as f: + assert 'gl-matrix' not in f.read(), 'Should not include glMatrix when not needed' + + def test_glbook(self): + programs = self.get_library('glbook', [ + os.path.join('Chapter_2', 'Hello_Triangle', 'CH02_HelloTriangle.bc'), + os.path.join('Chapter_8', 'Simple_VertexShader', 'CH08_SimpleVertexShader.bc'), + os.path.join('Chapter_9', 'Simple_Texture2D', 'CH09_SimpleTexture2D.bc'), + os.path.join('Chapter_9', 'Simple_TextureCubemap', 'CH09_TextureCubemap.bc'), + os.path.join('Chapter_9', 'TextureWrap', 'CH09_TextureWrap.bc'), + os.path.join('Chapter_10', 'MultiTexture', 'CH10_MultiTexture.bc'), + os.path.join('Chapter_13', 'ParticleSystem', 'CH13_ParticleSystem.bc'), + ], configure=None) + def book_path(*pathelems): + return path_from_root('tests', 'glbook', *pathelems) + for program in programs: + print program + basename = os.path.basename(program) + args = [] + if basename == 'CH10_MultiTexture.bc': + shutil.copyfile(book_path('Chapter_10', 'MultiTexture', 'basemap.tga'), os.path.join(self.get_dir(), 'basemap.tga')) + shutil.copyfile(book_path('Chapter_10', 'MultiTexture', 'lightmap.tga'), os.path.join(self.get_dir(), 'lightmap.tga')) + args = ['--preload-file', 'basemap.tga', '--preload-file', 'lightmap.tga'] + elif basename == 'CH13_ParticleSystem.bc': + shutil.copyfile(book_path('Chapter_13', 'ParticleSystem', 'smoke.tga'), os.path.join(self.get_dir(), 'smoke.tga')) + args = ['--preload-file', 'smoke.tga', '-O2'] # test optimizations and closure here as well for more coverage + + self.btest(program, + reference=book_path(basename.replace('.bc', '.png')), args=args) + + def test_gles2_emulation(self): + shutil.copyfile(path_from_root('tests', 'glbook', 'Chapter_10', 'MultiTexture', 'basemap.tga'), self.in_dir('basemap.tga')) + shutil.copyfile(path_from_root('tests', 'glbook', 'Chapter_10', 'MultiTexture', 'lightmap.tga'), self.in_dir('lightmap.tga')) + shutil.copyfile(path_from_root('tests', 'glbook', 'Chapter_13', 'ParticleSystem', 'smoke.tga'), self.in_dir('smoke.tga')) + + for source, reference in [ + (os.path.join('glbook', 'Chapter_2', 'Hello_Triangle', 'Hello_Triangle_orig.c'), path_from_root('tests', 'glbook', 'CH02_HelloTriangle.png')), + #(os.path.join('glbook', 'Chapter_8', 'Simple_VertexShader', 'Simple_VertexShader_orig.c'), path_from_root('tests', 'glbook', 'CH08_SimpleVertexShader.png')), # XXX needs INT extension in WebGL + (os.path.join('glbook', 'Chapter_9', 'TextureWrap', 'TextureWrap_orig.c'), path_from_root('tests', 'glbook', 'CH09_TextureWrap.png')), + #(os.path.join('glbook', 'Chapter_9', 'Simple_TextureCubemap', 'Simple_TextureCubemap_orig.c'), path_from_root('tests', 'glbook', 'CH09_TextureCubemap.png')), # XXX needs INT extension in WebGL + (os.path.join('glbook', 'Chapter_9', 'Simple_Texture2D', 'Simple_Texture2D_orig.c'), path_from_root('tests', 'glbook', 'CH09_SimpleTexture2D.png')), + (os.path.join('glbook', 'Chapter_10', 'MultiTexture', 'MultiTexture_orig.c'), path_from_root('tests', 'glbook', 'CH10_MultiTexture.png')), + (os.path.join('glbook', 'Chapter_13', 'ParticleSystem', 'ParticleSystem_orig.c'), path_from_root('tests', 'glbook', 'CH13_ParticleSystem.png')), + ]: + print source + self.btest(source, + reference=reference, + args=['-I' + path_from_root('tests', 'glbook', 'Common'), + path_from_root('tests', 'glbook', 'Common', 'esUtil.c'), + path_from_root('tests', 'glbook', 'Common', 'esShader.c'), + path_from_root('tests', 'glbook', 'Common', 'esShapes.c'), + path_from_root('tests', 'glbook', 'Common', 'esTransform.c'), + '-s', 'FULL_ES2=1', + '--preload-file', 'basemap.tga', '--preload-file', 'lightmap.tga', '--preload-file', 'smoke.tga']) + + def test_emscripten_api(self): + self.btest('emscripten_api_browser.cpp', '1', args=['-s', '''EXPORTED_FUNCTIONS=['_main', '_third']''']) + + def test_emscripten_api_infloop(self): + self.btest('emscripten_api_browser_infloop.cpp', '7') + + def test_emscripten_fs_api(self): + shutil.copyfile(path_from_root('tests', 'screenshot.png'), os.path.join(self.get_dir(), 'screenshot.png')) # preloaded *after* run + self.btest('emscripten_fs_api_browser.cpp', '1') + + def test_sdl_quit(self): + self.btest('sdl_quit.c', '1') + + def test_sdl_resize(self): + self.btest('sdl_resize.c', '1') + + def test_gc(self): + self.btest('browser_gc.cpp', '1') + + def test_glshaderinfo(self): + self.btest('glshaderinfo.cpp', '1') + + def test_glgetattachedshaders(self): + self.btest('glgetattachedshaders.c', '1') + + def test_sdlglshader(self): + self.btest('sdlglshader.c', reference='sdlglshader.png', args=['-O2', '--closure', '1']) + + def test_gl_ps(self): + # pointers and a shader + shutil.copyfile(path_from_root('tests', 'screenshot.png'), os.path.join(self.get_dir(), 'screenshot.png')) + self.btest('gl_ps.c', reference='gl_ps.png', args=['--preload-file', 'screenshot.png'], reference_slack=1) + + def test_gl_ps_packed(self): + # packed data that needs to be strided + shutil.copyfile(path_from_root('tests', 'screenshot.png'), os.path.join(self.get_dir(), 'screenshot.png')) + self.btest('gl_ps_packed.c', reference='gl_ps.png', args=['--preload-file', 'screenshot.png'], reference_slack=1) + + def test_gl_ps_strides(self): + shutil.copyfile(path_from_root('tests', 'screenshot.png'), os.path.join(self.get_dir(), 'screenshot.png')) + self.btest('gl_ps_strides.c', reference='gl_ps_strides.png', args=['--preload-file', 'screenshot.png']) + + def test_gl_renderers(self): + self.btest('gl_renderers.c', reference='gl_renderers.png', args=['-s', 'GL_UNSAFE_OPTS=0']) + + def test_gl_stride(self): + self.btest('gl_stride.c', reference='gl_stride.png', args=['-s', 'GL_UNSAFE_OPTS=0']) + + def test_matrix_identity(self): + self.btest('gl_matrix_identity.c', expected=['-1882984448', '460451840']) + + def test_cubegeom_pre(self): + self.btest('cubegeom_pre.c', reference='cubegeom_pre.png') + + def test_cubegeom_pre2(self): + self.btest('cubegeom_pre2.c', reference='cubegeom_pre2.png', args=['-s', 'GL_DEBUG=1']) # some coverage for GL_DEBUG not breaking the build + + def test_cubegeom_pre3(self): + self.btest('cubegeom_pre3.c', reference='cubegeom_pre2.png') + + def test_cubegeom(self): + self.btest('cubegeom.c', args=['-O2', '-g'], reference='cubegeom.png') + + def test_cubegeom_glew(self): + self.btest('cubegeom_glew.c', args=['-O2', '--closure', '1'], reference='cubegeom.png') + + def test_cubegeom_color(self): + self.btest('cubegeom_color.c', reference='cubegeom_color.png') + + def test_cubegeom_normal(self): + self.btest('cubegeom_normal.c', reference='cubegeom_normal.png') + + def test_cubegeom_normal_dap(self): # draw is given a direct pointer to clientside memory, no element array buffer + self.btest('cubegeom_normal_dap.c', reference='cubegeom_normal.png') + + def test_cubegeom_normal_dap_far(self): # indices do nto start from 0 + self.btest('cubegeom_normal_dap_far.c', reference='cubegeom_normal.png') + + def test_cubegeom_normal_dap_far_range(self): # glDrawRangeElements + self.btest('cubegeom_normal_dap_far_range.c', reference='cubegeom_normal.png') + + def test_cubegeom_normal_dap_far_glda(self): # use glDrawArrays + self.btest('cubegeom_normal_dap_far_glda.c', reference='cubegeom_normal_dap_far_glda.png') + + def test_cubegeom_normal_dap_far_glda_quad(self): # with quad + self.btest('cubegeom_normal_dap_far_glda_quad.c', reference='cubegeom_normal_dap_far_glda_quad.png') + + def test_cubegeom_mt(self): + self.btest('cubegeom_mt.c', reference='cubegeom_mt.png') # multitexture + + def test_cubegeom_color2(self): + self.btest('cubegeom_color2.c', reference='cubegeom_color2.png') + + def test_cubegeom_texturematrix(self): + self.btest('cubegeom_texturematrix.c', reference='cubegeom_texturematrix.png') + + def test_cubegeom_fog(self): + self.btest('cubegeom_fog.c', reference='cubegeom_fog.png') + + def test_cubegeom_pre_vao(self): + self.btest('cubegeom_pre_vao.c', reference='cubegeom_pre_vao.png') + + def test_cubegeom_pre2_vao(self): + self.btest('cubegeom_pre2_vao.c', reference='cubegeom_pre_vao.png') + + def test_cubegeom_pre2_vao2(self): + self.btest('cubegeom_pre2_vao2.c', reference='cubegeom_pre2_vao2.png') + + def test_cube_explosion(self): + self.btest('cube_explosion.c', reference='cube_explosion.png') + + def test_sdl_canvas_blank(self): + self.btest('sdl_canvas_blank.c', reference='sdl_canvas_blank.png') + + def test_sdl_canvas_palette(self): + self.btest('sdl_canvas_palette.c', reference='sdl_canvas_palette.png') + + def test_sdl_canvas_twice(self): + self.btest('sdl_canvas_twice.c', reference='sdl_canvas_twice.png') + + def test_sdl_maprgba(self): + self.btest('sdl_maprgba.c', reference='sdl_maprgba.png', reference_slack=3) + + def test_sdl_rotozoom(self): + shutil.copyfile(path_from_root('tests', 'screenshot.png'), os.path.join(self.get_dir(), 'screenshot.png')) + self.btest('sdl_rotozoom.c', reference='sdl_rotozoom.png', args=['--preload-file', 'screenshot.png'], reference_slack=5) + + def test_sdl_gfx_primitives(self): + self.btest('sdl_gfx_primitives.c', reference='sdl_gfx_primitives.png', reference_slack=1) + + def test_sdl_canvas_palette_2(self): + open(os.path.join(self.get_dir(), 'pre.js'), 'w').write(''' + Module['preRun'].push(function() { + SDL.defaults.copyOnLock = false; + }); + ''') + + open(os.path.join(self.get_dir(), 'args-r.js'), 'w').write(''' + Module['arguments'] = ['-r']; + ''') + + open(os.path.join(self.get_dir(), 'args-g.js'), 'w').write(''' + Module['arguments'] = ['-g']; + ''') + + open(os.path.join(self.get_dir(), 'args-b.js'), 'w').write(''' + Module['arguments'] = ['-b']; + ''') + + self.btest('sdl_canvas_palette_2.c', reference='sdl_canvas_palette_r.png', args=['--pre-js', 'pre.js', '--pre-js', 'args-r.js']) + self.btest('sdl_canvas_palette_2.c', reference='sdl_canvas_palette_g.png', args=['--pre-js', 'pre.js', '--pre-js', 'args-g.js']) + self.btest('sdl_canvas_palette_2.c', reference='sdl_canvas_palette_b.png', args=['--pre-js', 'pre.js', '--pre-js', 'args-b.js']) + + def test_sdl_alloctext(self): + self.btest('sdl_alloctext.c', expected='1', args=['-O2', '-s', 'TOTAL_MEMORY=' + str(1024*1024*8)]) + + def test_sdl_surface_refcount(self): + self.btest('sdl_surface_refcount.c', expected='1') + + def test_glbegin_points(self): + shutil.copyfile(path_from_root('tests', 'screenshot.png'), os.path.join(self.get_dir(), 'screenshot.png')) + self.btest('glbegin_points.c', reference='glbegin_points.png', args=['--preload-file', 'screenshot.png']) + + def test_s3tc(self): + shutil.copyfile(path_from_root('tests', 'screenshot.dds'), os.path.join(self.get_dir(), 'screenshot.dds')) + self.btest('s3tc.c', reference='s3tc.png', args=['--preload-file', 'screenshot.dds']) + + def test_s3tc_crunch(self): + shutil.copyfile(path_from_root('tests', 'ship.dds'), 'ship.dds') + shutil.copyfile(path_from_root('tests', 'bloom.dds'), 'bloom.dds') + shutil.copyfile(path_from_root('tests', 'water.dds'), 'water.dds') + Popen([PYTHON, FILE_PACKAGER, 'test.data', '--pre-run', '--crunch', '--preload', 'ship.dds', 'bloom.dds', 'water.dds'], stdout=open('pre.js', 'w')).communicate() + assert os.stat('test.data').st_size < 0.5*(os.stat('ship.dds').st_size+os.stat('bloom.dds').st_size+os.stat('water.dds').st_size), 'Compressed should be smaller than dds' + shutil.move('ship.dds', 'ship.donotfindme.dds') # make sure we load from the compressed + shutil.move('bloom.dds', 'bloom.donotfindme.dds') # make sure we load from the compressed + shutil.move('water.dds', 'water.donotfindme.dds') # make sure we load from the compressed + self.btest('s3tc_crunch.c', reference='s3tc_crunch.png', reference_slack=11, args=['--pre-js', 'pre.js']) + + def test_s3tc_crunch_split(self): # load several datafiles/outputs of file packager + shutil.copyfile(path_from_root('tests', 'ship.dds'), 'ship.dds') + shutil.copyfile(path_from_root('tests', 'bloom.dds'), 'bloom.dds') + shutil.copyfile(path_from_root('tests', 'water.dds'), 'water.dds') + Popen([PYTHON, FILE_PACKAGER, 'asset_a.data', '--pre-run', '--crunch', '--preload', 'ship.dds', 'bloom.dds'], stdout=open('asset_a.js', 'w')).communicate() + Popen([PYTHON, FILE_PACKAGER, 'asset_b.data', '--pre-run', '--crunch', '--preload', 'water.dds'], stdout=open('asset_b.js', 'w')).communicate() + shutil.move('ship.dds', 'ship.donotfindme.dds') # make sure we load from the compressed + shutil.move('bloom.dds', 'bloom.donotfindme.dds') # make sure we load from the compressed + shutil.move('water.dds', 'water.donotfindme.dds') # make sure we load from the compressed + self.btest('s3tc_crunch.c', reference='s3tc_crunch.png', reference_slack=11, args=['--pre-js', 'asset_a.js', '--pre-js', 'asset_b.js']) + + def test_aniso(self): + shutil.copyfile(path_from_root('tests', 'water.dds'), 'water.dds') + self.btest('aniso.c', reference='aniso.png', reference_slack=2, args=['--preload-file', 'water.dds']) + + def test_tex_nonbyte(self): + self.btest('tex_nonbyte.c', reference='tex_nonbyte.png') + + def test_float_tex(self): + self.btest('float_tex.cpp', reference='float_tex.png') + + def test_subdata(self): + self.btest('gl_subdata.cpp', reference='float_tex.png') + + def test_perspective(self): + self.btest('perspective.c', reference='perspective.png') + + def test_runtimelink(self): + return self.skip('shared libs are deprecated') + main, supp = self.setup_runtimelink_test() + + open(self.in_dir('supp.cpp'), 'w').write(supp) + Popen([PYTHON, EMCC, self.in_dir('supp.cpp'), '-o', 'supp.js', '-s', 'LINKABLE=1', '-s', 'NAMED_GLOBALS=1', '-s', 'BUILD_AS_SHARED_LIB=2', '-O2', '-s', 'ASM_JS=0']).communicate() + shutil.move(self.in_dir('supp.js'), self.in_dir('supp.so')) + + self.btest(main, args=['-s', 'LINKABLE=1', '-s', 'NAMED_GLOBALS=1', '-s', 'RUNTIME_LINKED_LIBS=["supp.so"]', '-DBROWSER=1', '-O2', '-s', 'ASM_JS=0'], expected='76') + + def test_pre_run_deps(self): + # Adding a dependency in preRun will delay run + open(os.path.join(self.get_dir(), 'pre.js'), 'w').write(''' + Module.preRun = function() { + addRunDependency(); + Module.print('preRun called, added a dependency...'); + setTimeout(function() { + Module.okk = 10; + removeRunDependency() + }, 2000); + }; + ''') + self.btest('pre_run_deps.cpp', expected='10', args=['--pre-js', 'pre.js']) + + def test_worker_api(self): + Popen([PYTHON, EMCC, path_from_root('tests', 'worker_api_worker.cpp'), '-o', 'worker.js', '-s', 'BUILD_AS_WORKER=1', '-s', 'EXPORTED_FUNCTIONS=["_one"]']).communicate() + self.btest('worker_api_main.cpp', expected='566') + + def test_worker_api_2(self): + Popen([PYTHON, EMCC, path_from_root('tests', 'worker_api_2_worker.cpp'), '-o', 'worker.js', '-s', 'BUILD_AS_WORKER=1', '-O2', '--minify', '0', '-s', 'EXPORTED_FUNCTIONS=["_one", "_two", "_three", "_four"]']).communicate() + self.btest('worker_api_2_main.cpp', args=['-O2', '--minify', '0'], expected='11') + + def test_emscripten_async_wget2(self): + self.btest('http.cpp', expected='0', args=['-I' + path_from_root('tests')]) diff --git a/tests/test_core.py b/tests/test_core.py new file mode 100644 index 0000000000000..31db6ca52296d --- /dev/null +++ b/tests/test_core.py @@ -0,0 +1,10074 @@ +# coding=utf-8 + +import glob, hashlib, os, re, shutil, subprocess, sys +import tools.shared +from tools.shared import * +from runner import RunnerCore, path_from_root, checked_sanity, test_modes + +class T(RunnerCore): # Short name, to make it more fun to use manually on the commandline + def is_le32(self): + return not ('i386-pc-linux-gnu' in COMPILER_OPTS or self.env.get('EMCC_LLVM_TARGET') == 'i386-pc-linux-gnu') + + def test_hello_world(self): + src = ''' + #include + int main() + { + printf("hello, world!\\n"); + return 0; + } + ''' + self.do_run(src, 'hello, world!') + + assert 'EMSCRIPTEN_GENERATED_FUNCTIONS' not in open(self.in_dir('src.cpp.o.js')).read(), 'must not emit this unneeded internal thing' + + def test_intvars(self): + if self.emcc_args == None: return self.skip('needs ta2') + + src = ''' + #include + int global = 20; + int *far; + int main() + { + int x = 5; + int y = x+17; + int z = (y-1)/2; // Should stay an integer after division! + y += 1; + int w = x*3+4; + int k = w < 15 ? 99 : 101; + far = &k; + *far += global; + int i = k > 100; // Should be an int, not a bool! + int j = i << 6; + j >>= 1; + j = j ^ 5; + int h = 1; + h |= 0; + int p = h; + p &= 0; + printf("*%d,%d,%d,%d,%d,%d,%d,%d,%d*\\n", x, y, z, w, k, i, j, h, p); + + long hash = -1; + size_t perturb; + int ii = 0; + for (perturb = hash; ; perturb >>= 5) { + printf("%d:%d", ii, perturb); + ii++; + if (ii == 9) break; + printf(","); + } + printf("*\\n"); + printf("*%.1d,%.2d*\\n", 56, 9); + + // Fixed-point math on 64-bit ints. Tricky to support since we have no 64-bit shifts in JS + { + struct Fixed { + static int Mult(int a, int b) { + return ((long long)a * (long long)b) >> 16; + } + }; + printf("fixed:%d\\n", Fixed::Mult(150000, 140000)); + } + + printf("*%ld*%p\\n", (long)21, &hash); // The %p should not enter an infinite loop! + return 0; + } + ''' + self.do_run(src, '*5,23,10,19,121,1,37,1,0*\n0:-1,1:134217727,2:4194303,3:131071,4:4095,5:127,6:3,7:0,8:0*\n*56,09*\nfixed:320434\n*21*') + + def test_sintvars(self): + Settings.CORRECT_SIGNS = 1 # Relevant to this test + src = ''' + #include + struct S { + char *match_start; + char *strstart; + }; + int main() + { + struct S _s; + struct S *s = &_s; + unsigned short int sh; + + s->match_start = (char*)32522; + s->strstart = (char*)(32780); + printf("*%d,%d,%d*\\n", (int)s->strstart, (int)s->match_start, (int)(s->strstart - s->match_start)); + sh = s->strstart - s->match_start; + printf("*%d,%d*\\n", sh, sh>>7); + + s->match_start = (char*)32999; + s->strstart = (char*)(32780); + printf("*%d,%d,%d*\\n", (int)s->strstart, (int)s->match_start, (int)(s->strstart - s->match_start)); + sh = s->strstart - s->match_start; + printf("*%d,%d*\\n", sh, sh>>7); + } + ''' + output = '*32780,32522,258*\n*258,2*\n*32780,32999,-219*\n*65317,510*' + Settings.CORRECT_OVERFLOWS = 0 # We should not need overflow correction to get this right + self.do_run(src, output, force_c=True) + + def test_i64(self): + if Settings.USE_TYPED_ARRAYS != 2: return self.skip('i64 mode 1 requires ta2') + + src = ''' + #include + int main() + { + long long a = 0x2b00505c10; + long long b = a >> 29; + long long c = a >> 32; + long long d = a >> 34; + printf("*%Ld,%Ld,%Ld,%Ld*\\n", a, b, c, d); + unsigned long long ua = 0x2b00505c10; + unsigned long long ub = ua >> 29; + unsigned long long uc = ua >> 32; + unsigned long long ud = ua >> 34; + printf("*%Ld,%Ld,%Ld,%Ld*\\n", ua, ub, uc, ud); + + long long x = 0x0000def123450789ULL; // any bigger than this, and we + long long y = 0x00020ef123456089ULL; // start to run into the double precision limit! + printf("*%Ld,%Ld,%Ld,%Ld,%Ld*\\n", x, y, x | y, x & y, x ^ y, x >> 2, y << 2); + + printf("*"); + long long z = 13; + int n = 0; + while (z > 1) { + printf("%.2f,", (float)z); // these must be integers! + z = z >> 1; + n++; + } + printf("*%d*\\n", n); + return 0; + } + ''' + self.do_run(src, '*184688860176,344,43,10*\n*184688860176,344,43,10*\n*245127260211081,579378795077769,808077213656969,16428841631881,791648372025088*\n*13.00,6.00,3.00,*3*') + + src = r''' + #include + #include + #include + + int64_t returner1() { return 0x0000def123450789ULL; } + int64_t returner2(int test) { + while (test > 10) test /= 2; // confuse the compiler so it doesn't eliminate this function + return test > 5 ? 0x0000def123450123ULL : 0ULL; + } + + void modifier1(int64_t t) { + t |= 12; + printf("m1: %Ld\n", t); + } + void modifier2(int64_t &t) { + t |= 12; + } + + int truthy() { + int x = time(0); + while (x > 10) { + x |= 7; + x /= 2; + } + return x < 3; + } + + struct IUB { + int c; + long long d; + }; + + IUB iub[] = { + { 55, 17179869201 }, + { 122, 25769803837 }, + }; + + int main(int argc, char **argv) + { + int64_t x1 = 0x1234def123450789ULL; + int64_t x2 = 0x1234def123450788ULL; + int64_t x3 = 0x1234def123450789ULL; + printf("*%Ld\n%d,%d,%d,%d,%d\n%d,%d,%d,%d,%d*\n", x1, x1==x2, x1x2, x1>=x2, // note: some rounding in the printing! + x1==x3, x1x3, x1>=x3); + printf("*%Ld*\n", returner1()); + printf("*%Ld*\n", returner2(30)); + + uint64_t maxx = -1ULL; + printf("*%Lu*\n*%Lu*\n", maxx, maxx >> 5); + + // Make sure params are not modified if they shouldn't be + int64_t t = 123; + modifier1(t); + printf("*%Ld*\n", t); + modifier2(t); + printf("*%Ld*\n", t); + + // global structs with i64s + printf("*%d,%Ld*\n*%d,%Ld*\n", iub[0].c, iub[0].d, iub[1].c, iub[1].d); + + // Bitshifts + { + int64_t a = -1; + int64_t b = a >> 29; + int64_t c = a >> 32; + int64_t d = a >> 34; + printf("*%Ld,%Ld,%Ld,%Ld*\n", a, b, c, d); + uint64_t ua = -1; + int64_t ub = ua >> 29; + int64_t uc = ua >> 32; + int64_t ud = ua >> 34; + printf("*%Ld,%Ld,%Ld,%Ld*\n", ua, ub, uc, ud); + } + + // Nonconstant bitshifts + { + int64_t a = -1; + int64_t b = a >> (29 - argc + 1); + int64_t c = a >> (32 - argc + 1); + int64_t d = a >> (34 - argc + 1); + printf("*%Ld,%Ld,%Ld,%Ld*\n", a, b, c, d); + uint64_t ua = -1; + int64_t ub = ua >> (29 - argc + 1); + int64_t uc = ua >> (32 - argc + 1); + int64_t ud = ua >> (34 - argc + 1); + printf("*%Ld,%Ld,%Ld,%Ld*\n", ua, ub, uc, ud); + } + + // Math mixtures with doubles + { + uint64_t a = 5; + double b = 6.8; + uint64_t c = a * b; + if (truthy()) printf("*%d,%d,%d*\n", (int)&a, (int)&b, (int)&c); // printing addresses prevents optimizations + printf("*prod:%llu*\n", c); + } + + // Basic (rounded, for now) math. Just check compilation. + int64_t a = 0x1234def123450789ULL; + a--; if (truthy()) a--; // confuse optimizer + int64_t b = 0x1234000000450789ULL; + b++; if (truthy()) b--; // confuse optimizer + printf("*%Ld,%Ld,%Ld,%Ld*\n", (a+b)/5000, (a-b)/5000, (a*3)/5000, (a/5)/5000); + + a -= 17; if (truthy()) a += 5; // confuse optimizer + b -= 17; if (truthy()) b += 121; // confuse optimizer + printf("*%Lx,%Lx,%Lx,%Lx*\n", b - a, b - a/2, b/2 - a, b - 20); + + if (truthy()) a += 5/b; // confuse optimizer + if (truthy()) b += 121*(3+a/b); // confuse optimizer + printf("*%Lx,%Lx,%Lx,%Lx*\n", a - b, a - b/2, a/2 - b, a - 20); + + return 0; + } + ''' + self.do_run(src, '*1311918518731868041\n' + + '0,0,0,1,1\n' + + '1,0,1,0,1*\n' + + '*245127260211081*\n' + + '*245127260209443*\n' + + '*18446744073709551615*\n' + + '*576460752303423487*\n' + + 'm1: 127\n' + + '*123*\n' + + '*127*\n' + + '*55,17179869201*\n' + + '*122,25769803837*\n' + + '*-1,-1,-1,-1*\n' + + '*-1,34359738367,4294967295,1073741823*\n' + + '*-1,-1,-1,-1*\n' + + '*-1,34359738367,4294967295,1073741823*\n' + + '*prod:34*\n' + + '*524718382041609,49025451137,787151111239120,52476740749274*\n' + + '*ffff210edd000002,91990876ea283be,f6e5210edcdd7c45,1234000000450765*\n' + + '*def122fffffe,91adef1232283bb,f6e66f78915d7c42,1234def123450763*\n') + + src = r''' + #include + #include + + int main() + { + long long i,j,k; + + i = 0; + j = -1, + k = 1; + + printf( "*\n" ); + printf( "%s\n", i > j ? "Ok": "Fail" ); + printf( "%s\n", k > i ? "Ok": "Fail" ); + printf( "%s\n", k > j ? "Ok": "Fail" ); + printf( "%s\n", i < j ? "Fail": "Ok" ); + printf( "%s\n", k < i ? "Fail": "Ok" ); + printf( "%s\n", k < j ? "Fail": "Ok" ); + printf( "%s\n", (i-j) >= k ? "Ok": "Fail" ); + printf( "%s\n", (i-j) <= k ? "Ok": "Fail" ); + printf( "%s\n", i > std::numeric_limits::min() ? "Ok": "Fail" ); + printf( "%s\n", i < std::numeric_limits::max() ? "Ok": "Fail" ); + printf( "*\n" ); + } + ''' + + self.do_run(src, '*\nOk\nOk\nOk\nOk\nOk\nOk\nOk\nOk\nOk\nOk\n*') + + # stuff that also needs sign corrections + + Settings.CORRECT_SIGNS = 1 + + src = r''' + #include + #include + + int main() + { + // i32 vs i64 + int32_t small = -1; + int64_t large = -1; + printf("*%d*\n", small == large); + small++; + printf("*%d*\n", small == large); + uint32_t usmall = -1; + uint64_t ularge = -1; + printf("*%d*\n", usmall == ularge); + return 0; + } + ''' + + self.do_run(src, '*1*\n*0*\n*0*\n') + + def test_i64_b(self): + if Settings.USE_TYPED_ARRAYS != 2: return self.skip('full i64 stuff only in ta2') + + src = r''' + #include + #include + + typedef long long int64; + + #define PRMJ_USEC_PER_SEC 1000000L + + int main(int argc, char * argv[]) { + int64 sec = 1329409675 + argc; + int64 usec = 2329509675; + int64 mul = int64(sec) * PRMJ_USEC_PER_SEC; + int64 add = mul + int64(usec); + int add_low = add; + int add_high = add >> 32; + printf("*%lld,%lld,%u,%u*\n", mul, add, add_low, add_high); + int64 x = sec + (usec << 25); + x >>= argc*3; + printf("*%llu*\n", x); + return 0; + } + ''' + + self.do_run(src, '*1329409676000000,1329412005509675,3663280683,309527*\n*9770671914067409*\n') + + def test_i64_cmp(self): + if Settings.USE_TYPED_ARRAYS != 2: return self.skip('full i64 stuff only in ta2') + + src = r''' + #include + + typedef long long int64; + + bool compare(int64 val) { + return val == -12; + } + + bool compare2(int64 val) { + return val < -12; + } + + int main(int argc, char * argv[]) { + printf("*%d,%d,%d,%d,%d,%d*\n", argc, compare(argc-1-12), compare(1000+argc), compare2(argc-1-10), compare2(argc-1-14), compare2(argc+1000)); + return 0; + } + ''' + + self.do_run(src, '*1,1,0,0,1,0*\n') + + def test_i64_cmp2(self): + if Settings.USE_TYPED_ARRAYS != 2: return self.skip('full i64 stuff only in ta2') + + src = r''' + #include + #include + + typedef int32_t INT32; + typedef int64_t INT64; + typedef uint8_t UINT8; + + void interface_clock_changed() + { + UINT8 m_divshift; + INT32 m_divisor; + + //INT64 attos = m_attoseconds_per_cycle; + INT64 attos = 279365114840; + m_divshift = 0; + while (attos >= (1UL << 31)) + { + m_divshift++; + printf("m_divshift is %i, on %Ld >?= %lu\n", m_divshift, attos, 1UL << 31); + attos >>= 1; + } + m_divisor = attos; + + printf("m_divisor is %i\n",m_divisor); + } + + int main() { + interface_clock_changed(); + return 0; + } + ''' + self.do_run(src, '''m_divshift is 1, on 279365114840 >?= 2147483648 +m_divshift is 2, on 139682557420 >?= 2147483648 +m_divshift is 3, on 69841278710 >?= 2147483648 +m_divshift is 4, on 34920639355 >?= 2147483648 +m_divshift is 5, on 17460319677 >?= 2147483648 +m_divshift is 6, on 8730159838 >?= 2147483648 +m_divshift is 7, on 4365079919 >?= 2147483648 +m_divshift is 8, on 2182539959 >?= 2147483648 +m_divisor is 1091269979 +''') + + def test_i64_double(self): + if Settings.USE_TYPED_ARRAYS != 2: return self.skip('full i64 stuff only in ta2') + + + src = r''' + #include + + typedef long long int64; + #define JSDOUBLE_HI32_SIGNBIT 0x80000000 + + bool JSDOUBLE_IS_NEGZERO(double d) + { + union { + struct { + unsigned int lo, hi; + } s; + double d; + } x; + if (d != 0) + return false; + x.d = d; + return (x.s.hi & JSDOUBLE_HI32_SIGNBIT) != 0; + } + + bool JSINT64_IS_NEGZERO(int64 l) + { + union { + int64 i; + double d; + } x; + if (l != 0) + return false; + x.i = l; + return x.d == -0; + } + + int main(int argc, char * argv[]) { + printf("*%d,%d,%d,%d*\n", JSDOUBLE_IS_NEGZERO(0), JSDOUBLE_IS_NEGZERO(-0), JSDOUBLE_IS_NEGZERO(-1), JSDOUBLE_IS_NEGZERO(+1)); + printf("*%d,%d,%d,%d*\n", JSINT64_IS_NEGZERO(0), JSINT64_IS_NEGZERO(-0), JSINT64_IS_NEGZERO(-1), JSINT64_IS_NEGZERO(+1)); + return 0; + } + ''' + self.do_run(src, '*0,0,0,0*\n*1,1,0,0*\n') # same as gcc + + def test_i64_umul(self): + if Settings.USE_TYPED_ARRAYS != 2: return self.skip('full i64 stuff only in ta2') + + src = r''' + #include + #include + + typedef uint32_t UINT32; + typedef uint64_t UINT64; + + int main() { + volatile UINT32 testu32a = 2375724032U; + UINT32 bigu32 = 0xffffffffU; + volatile UINT64 testu64a = 14746250828952703000U; + + while ((UINT64)testu32a * (UINT64)bigu32 < testu64a) { + printf("testu64a is %llu\n", testu64a); + testu64a /= 2; + } + + return 0; + } + ''' + self.do_run(src, 'testu64a is 14746250828952703000\n') + + def test_i64_precise(self): + if Settings.USE_TYPED_ARRAYS != 2: return self.skip('full i64 stuff only in ta2') + + src = r''' + #include + #include + + int main() { + uint64_t x = 0, y = 0; + for (int i = 0; i < 64; i++) { + x += 1ULL << i; + y += x; + x /= 3; + y *= 5; + printf("unsigned %d: %llu,%llu,%llu,%llu,%llu,%llu,%llu,%llu,%llu\n", i, x, y, x+y, x-y, x*y, y ? x/y : 0, x ? y/x : 0, y ? x%y : 0, x ? y%x : 0); + } + int64_t x2 = 0, y2 = 0; + for (int i = 0; i < 64; i++) { + x2 += 1LL << i; + y2 += x2; + x2 /= 3 * (i % 7 ? -1 : 1); + y2 *= 5 * (i % 2 ? -1 : 1); + printf("signed %d: %lld,%lld,%lld,%lld,%lld,%lld,%lld,%lld,%lld\n", i, x2, y2, x2+y2, x2-y2, x2*y2, y2 ? x2/y2 : 0, x2 ? y2/x2 : 0, y2 ? x2%y2 : 0, x2 ? y2%x2 : 0); + } + return 0; + } + ''' + self.do_run(src, open(path_from_root('tests', 'i64_precise.txt')).read()) + + # Verify that even if we ask for precision, if it is not needed it is not included + Settings.PRECISE_I64_MATH = 1 + src = ''' + #include + #include + + int main(int argc, char **argv) { + uint64_t x = 2125299906845564, y = 1225891506842664; + if (argc == 12) { + x = x >> 1; + y = y >> 1; + } + x = x & 12ULL; + y = y | 12ULL; + x = x ^ y; + x <<= 2; + y >>= 3; + printf("*%llu, %llu*\\n", x, y); + } + ''' + self.do_run(src, '*4903566027370624, 153236438355333*') + code = open(os.path.join(self.get_dir(), 'src.cpp.o.js')).read() + assert 'goog.math.Long' not in code, 'i64 precise math should not have been included if not actually used' + + # But if we force it to be included, it is. First, a case where we don't need it + Settings.PRECISE_I64_MATH = 2 + self.do_run(open(path_from_root('tests', 'hello_world.c')).read(), 'hello') + code = open(os.path.join(self.get_dir(), 'src.cpp.o.js')).read() + assert 'goog.math.Long' in code, 'i64 precise math should be included if forced' + + # and now one where we do + self.do_run(r''' + #include + + int main( int argc, char ** argv ) + { + unsigned long a = 0x60DD1695U; + unsigned long b = 0xCA8C4E7BU; + unsigned long long c = (unsigned long long)a * b; + printf( "c = %016llx\n", c ); + + return 0; + } + ''', 'c = 4ca38a6bd2973f97') + + def test_i64_llabs(self): + if Settings.USE_TYPED_ARRAYS != 2: return self.skip('full i64 stuff only in ta2') + Settings.PRECISE_I64_MATH = 2 + self.do_run(r''' + #include + #include + + int main(int argc, char ** argv) { + printf("%lld,%lld\n", llabs(-576460752303423489), llabs(576460752303423489)); + return 0; + } + ''', '576460752303423489,576460752303423489') + + def test_i64_zextneg(self): + if Settings.USE_TYPED_ARRAYS != 2: return self.skip('full i64 stuff only in ta2') + + src = r''' + #include + #include + + int main(int argc, char *argv[]) + { + uint8_t byte = 0x80; + uint16_t two = byte; + uint32_t four = byte; + uint64_t eight = byte; + + printf("value: %d,%d,%d,%lld.\n", byte, two, four, eight); + + return 0; + } + ''' + self.do_run(src, 'value: 128,128,128,128.') + + def test_i64_7z(self): + if Settings.USE_TYPED_ARRAYS != 2: return self.skip('full i64 stuff only in ta2') + + src = r''' + #include + #include + uint64_t a, b; + int main(int argc, char *argv[]) + { + a = argc; + b = argv[1][0]; + printf("%d,%d\n", a, b); + if (a > a + b || a > a + b + 1) { + printf("one %lld, %lld", a, b); + return 0; + } + printf("zero %lld, %lld", a, b); + return 0; + } + ''' + self.do_run(src, 'zero 2, 104', ['hallo']) + + def test_i64_i16(self): + if Settings.USE_TYPED_ARRAYS != 2: return self.skip('full i64 stuff only in ta2') + + src = r''' + #include + #include + int main(int argc, char ** argv){ + int y=-133; + int64_t x= ((int64_t)((short)(y)))*(100 + argc); + if(x>0) + printf(">0\n"); + else + printf("<=0\n"); + } + ''' + self.do_run(src, '<=0') + + def test_i64_qdouble(self): + if Settings.USE_TYPED_ARRAYS != 2: return self.skip('full i64 stuff only in ta2') + + src = r''' + #include + typedef long long qint64; /* 64 bit signed */ + typedef double qreal; + + + int main(int argc, char **argv) + { + qreal c = 111; + qint64 d = -111 + (argc - 1); + c += d; + if (c < -1 || c > 1) + { + printf("Failed!\n"); + } + else + { + printf("Succeeded!\n"); + } + }; + ''' + self.do_run(src, 'Succeeded!') + + def test_i64_varargs(self): + if Settings.USE_TYPED_ARRAYS != 2: return self.skip('full i64 stuff only in ta2') + + src = r''' + #include + #include + #include + + int64_t ccv_cache_generate_signature(char *msg, int len, int64_t sig_start, ...) { + if (sig_start < 10123) + printf("%s\n", msg+len); + va_list v; + va_start(v, sig_start); + if (sig_start > 1413) + printf("%d\n", va_arg(v, int)); + else + printf("nada\n"); + va_end(v); + return len*sig_start*(msg[0]+1); + } + + int main(int argc, char **argv) + { + for (int i = 0; i < argc; i++) { + int64_t x; + if (i % 123123 == 0) + x = ccv_cache_generate_signature(argv[i], i+2, (int64_t)argc*argc, 54.111); + else + x = ccv_cache_generate_signature(argv[i], i+2, (int64_t)argc*argc, 13); + printf("%lld\n", x); + } + }; + ''' + self.do_run(src, '''in/this.program +nada +1536 +a +nada +5760 +fl +nada +6592 +sdfasdfasdf +nada +7840 +''', 'waka fleefl asdfasdfasdfasdf'.split(' ')) + + def test_i32_mul_precise(self): + if self.emcc_args == None: return self.skip('needs ta2') + + src = r''' + #include + + int main(int argc, char **argv) { + unsigned long d1 = 0x847c9b5d; + unsigned long q = 0x549530e1; + if (argc > 1000) { q += argc; d1 -= argc; } // confuse optimizer + printf("%lu\n", d1*q); + return 0; + } + ''' + self.do_run(src, '3217489085') + + def test_i32_mul_semiprecise(self): + if Settings.ASM_JS: return self.skip('asm is always fully precise') + + Settings.PRECISE_I32_MUL = 0 # we want semiprecise here + + src = r''' + #include + + typedef unsigned int uint; + + // from cube2, zlib licensed + + #define N (624) + #define M (397) + #define K (0x9908B0DFU) + + static uint state[N]; + static int next = N; + + void seedMT(uint seed) + { + state[0] = seed; + for(uint i = 1; i < N; i++) // if we do not do this precisely, at least we should coerce to int immediately, not wait + state[i] = seed = 1812433253U * (seed ^ (seed >> 30)) + i; + next = 0; + } + + int main() { + seedMT(5497); + for (int i = 0; i < 10; i++) printf("%d: %u\n", i, state[i]); + return 0; + } + ''' + self.do_run(src, '''0: 5497 +1: 2916432318 +2: 2502517762 +3: 3151524867 +4: 2323729668 +5: 2053478917 +6: 2409490438 +7: 848473607 +8: 691103752 +9: 3915535113 +''') + + def test_i16_emcc_intrinsic(self): + Settings.CORRECT_SIGNS = 1 # Relevant to this test + + src = r''' + #include + + int test(unsigned short a, unsigned short b) { + unsigned short result = a; + result += b; + if (result < b) printf("C!"); + return result; + } + + int main(void) { + printf(",%d,", test(0, 0)); + printf(",%d,", test(1, 1)); + printf(",%d,", test(65535, 1)); + printf(",%d,", test(1, 65535)); + printf(",%d,", test(32768, 32767)); + printf(",%d,", test(32768, 32768)); + return 0; + } + ''' + self.do_run(src, ',0,,2,C!,0,C!,0,,65535,C!,0,') + + def test_negative_zero(self): + src = r''' + #include + #include + + int main() { + #define TEST(x, y) \ + printf("%.2f, %.2f ==> %.2f\n", x, y, copysign(x, y)); + TEST( 5.0f, 5.0f); + TEST( 5.0f, -5.0f); + TEST(-5.0f, 5.0f); + TEST(-5.0f, -5.0f); + TEST( 5.0f, 4.0f); + TEST( 5.0f, -4.0f); + TEST(-5.0f, 4.0f); + TEST(-5.0f, -4.0f); + TEST( 0.0f, 5.0f); + TEST( 0.0f, -5.0f); + TEST(-0.0f, 5.0f); + TEST(-0.0f, -5.0f); + TEST( 5.0f, 0.0f); + TEST( 5.0f, -0.0f); + TEST(-5.0f, 0.0f); + TEST(-5.0f, -0.0f); + TEST( 0.0f, 0.0f); + TEST( 0.0f, -0.0f); + TEST(-0.0f, 0.0f); + TEST(-0.0f, -0.0f); + return 0; + } + ''' + self.do_run(src, '''5.00, 5.00 ==> 5.00 +5.00, -5.00 ==> -5.00 +-5.00, 5.00 ==> 5.00 +-5.00, -5.00 ==> -5.00 +5.00, 4.00 ==> 5.00 +5.00, -4.00 ==> -5.00 +-5.00, 4.00 ==> 5.00 +-5.00, -4.00 ==> -5.00 +0.00, 5.00 ==> 0.00 +0.00, -5.00 ==> -0.00 +-0.00, 5.00 ==> 0.00 +-0.00, -5.00 ==> -0.00 +5.00, 0.00 ==> 5.00 +5.00, -0.00 ==> -5.00 +-5.00, 0.00 ==> 5.00 +-5.00, -0.00 ==> -5.00 +0.00, 0.00 ==> 0.00 +0.00, -0.00 ==> -0.00 +-0.00, 0.00 ==> 0.00 +-0.00, -0.00 ==> -0.00 +''') + + def test_llvm_intrinsics(self): + if self.emcc_args == None: return self.skip('needs ta2') + + Settings.PRECISE_I64_MATH = 2 # for bswap64 + + src = r''' + #include + #include + + extern "C" { + extern unsigned short llvm_bswap_i16(unsigned short x); + extern unsigned int llvm_bswap_i32(unsigned int x); + extern int32_t llvm_ctlz_i32(int32_t x); + extern int64_t llvm_ctlz_i64(int64_t x); + extern int32_t llvm_cttz_i32(int32_t x); + extern int64_t llvm_cttz_i64(int64_t x); + extern int32_t llvm_ctpop_i32(int32_t x); + extern int64_t llvm_ctpop_i64(int64_t x); + extern int llvm_expect_i32(int x, int y); + } + + int main(void) { + unsigned short x = 0xc8ef; + printf("%x,%x\n", x&0xff, x >> 8); + x = llvm_bswap_i16(x); + printf("%x,%x\n", x&0xff, x >> 8); + + unsigned int y = 0xc5de158a; + printf("%x,%x,%x,%x\n", y&0xff, (y>>8)&0xff, (y>>16)&0xff, (y>>24)&0xff); + y = llvm_bswap_i32(y); + printf("%x,%x,%x,%x\n", y&0xff, (y>>8)&0xff, (y>>16)&0xff, (y>>24)&0xff); + + printf("%d,%d\n", (int)llvm_ctlz_i64(((int64_t)1) << 40), llvm_ctlz_i32(1<<10)); + printf("%d,%d\n", (int)llvm_cttz_i64(((int64_t)1) << 40), llvm_cttz_i32(1<<10)); + printf("%d,%d\n", (int)llvm_ctpop_i64((0x3101ULL << 32) | 1), llvm_ctpop_i32(0x3101)); + printf("%d\n", (int)llvm_ctpop_i32(-594093059)); + + printf("%d\n", llvm_expect_i32(x % 27, 3)); + + int64_t a = 1; + a = __builtin_bswap64(a); + printf("%lld\n", a); + + return 0; + } + ''' + self.do_run(src, '''ef,c8 +c8,ef +8a,15,de,c5 +c5,de,15,8a +23,21 +40,10 +5,4 +22 +13 +72057594037927936 +''') + + def test_bswap64(self): + if Settings.USE_TYPED_ARRAYS != 2: return self.skip('needs ta2') + + src = r''' + #include + #include + + #include + #include + #include + + typedef unsigned long long quint64; + + using namespace std; + + inline quint64 qbswap(quint64 source) + { + return 0 + | ((source & quint64(0x00000000000000ffLL)) << 56) + | ((source & quint64(0x000000000000ff00LL)) << 40) + | ((source & quint64(0x0000000000ff0000LL)) << 24) + | ((source & quint64(0x00000000ff000000LL)) << 8) + | ((source & quint64(0x000000ff00000000LL)) >> 8) + | ((source & quint64(0x0000ff0000000000LL)) >> 24) + | ((source & quint64(0x00ff000000000000LL)) >> 40) + | ((source & quint64(0xff00000000000000LL)) >> 56); + } + + int main() + { + quint64 v = strtoull("4433ffeeddccbb00", NULL, 16); + printf("%lld\n", v); + + const string string64bitInt = "4433ffeeddccbb00"; + stringstream s(string64bitInt); + quint64 int64bitInt = 0; + printf("1\n"); + s >> hex >> int64bitInt; + printf("2\n"); + + stringstream out; + out << hex << qbswap(int64bitInt); + + cout << out.str() << endl; + cout << hex << int64bitInt << endl; + cout << string64bitInt << endl; + + if (out.str() != "bbccddeeff3344") + { + cout << "Failed!" << endl; + } + else + { + cout << "Succeeded!" << endl; + } + + return 0; + } + ''' + self.do_run(src, '''4914553019779824384 +1 +2 +bbccddeeff3344 +4433ffeeddccbb00 +4433ffeeddccbb00 +Succeeded! +''') + + def test_sha1(self): + if self.emcc_args == None: return self.skip('needs ta2') + + self.do_run(open(path_from_root('tests', 'sha1.c')).read(), 'SHA1=15dd99a1991e0b3826fede3deffc1feba42278e6') + + def test_cube2md5(self): + if self.emcc_args == None: return self.skip('needs emcc') + self.emcc_args += ['--embed-file', 'cube2md5.txt'] + shutil.copyfile(path_from_root('tests', 'cube2md5.txt'), os.path.join(self.get_dir(), 'cube2md5.txt')) + self.do_run(open(path_from_root('tests', 'cube2md5.cpp')).read(), open(path_from_root('tests', 'cube2md5.ok')).read()) + + def test_cube2hash(self): + + try: + old_chunk_size = os.environ.get('EMSCRIPT_MAX_CHUNK_SIZE') or '' + os.environ['EMSCRIPT_MAX_CHUNK_SIZE'] = '1' # test splitting out each function to a chunk in emscripten.py (21 functions here) + + # A good test of i64 math + if Settings.USE_TYPED_ARRAYS != 2: return self.skip('requires ta2 C-style memory aliasing') + self.do_run('', 'Usage: hashstring ', + libraries=self.get_library('cube2hash', ['cube2hash.bc'], configure=None), + includes=[path_from_root('tests', 'cube2hash')]) + + for text, output in [('fleefl', '892BDB6FD3F62E863D63DA55851700FDE3ACF30204798CE9'), + ('fleefl2', 'AA2CC5F96FC9D540CA24FDAF1F71E2942753DB83E8A81B61'), + ('64bitisslow', '64D8470573635EC354FEE7B7F87C566FCAF1EFB491041670')]: + self.do_run('', 'hash value: ' + output, [text], no_build=True) + finally: + os.environ['EMSCRIPT_MAX_CHUNK_SIZE'] = old_chunk_size + + def test_unaligned(self): + if Settings.QUANTUM_SIZE == 1: return self.skip('No meaning to unaligned addresses in q1') + + src = r''' + #include + + struct S { + double x; + int y; + }; + + int main() { + // the 64-bit value here will not be 8-byte aligned + S s0[3] = { {0x12a751f430142, 22}, {0x17a5c85bad144, 98}, {1, 1}}; + char buffer[10*sizeof(S)]; + int b = int(buffer); + S *s = (S*)(b + 4-b%8); + s[0] = s0[0]; + s[1] = s0[1]; + s[2] = s0[2]; + + printf("*%d : %d : %d\n", sizeof(S), ((unsigned int)&s[0]) % 8 != ((unsigned int)&s[1]) % 8, + ((unsigned int)&s[1]) - ((unsigned int)&s[0])); + s[0].x++; + s[0].y++; + s[1].x++; + s[1].y++; + printf("%.1f,%d,%.1f,%d\n", s[0].x, s[0].y, s[1].x, s[1].y); + return 0; + } + ''' + + # TODO: A version of this with int64s as well + + if self.is_le32(): + return self.skip('LLVM marks the reads of s as fully aligned, making this test invalid') + else: + self.do_run(src, '*12 : 1 : 12\n328157500735811.0,23,416012775903557.0,99\n') + + return # TODO: continue to the next part here + + # Test for undefined behavior in C. This is not legitimate code, but does exist + + if Settings.USE_TYPED_ARRAYS != 2: return self.skip('No meaning to unaligned addresses without t2') + + src = r''' + #include + + int main() + { + int x[10]; + char *p = (char*)&x[0]; + p++; + short *q = (short*)p; + *q = 300; + printf("*%d:%d*\n", *q, ((int)q)%2); + int *r = (int*)p; + *r = 515559; + printf("*%d*\n", *r); + long long *t = (long long*)p; + *t = 42949672960; + printf("*%Ld*\n", *t); + return 0; + } + ''' + + try: + self.do_run(src, '*300:1*\n*515559*\n*42949672960*\n') + except Exception, e: + assert 'must be aligned' in str(e), e # expected to fail without emulation + + def test_align64(self): + src = r''' + #include + + // inspired by poppler + + enum Type { + A = 10, + B = 20 + }; + + struct Object { + Type type; + union { + int intg; + double real; + char *name; + }; + }; + + struct Principal { + double x; + Object a; + double y; + }; + + int main(int argc, char **argv) + { + int base = argc-1; + Object *o = NULL; + printf("%d,%d\n", sizeof(Object), sizeof(Principal)); + printf("%d,%d,%d,%d\n", (int)&o[base].type, (int)&o[base].intg, (int)&o[base].real, (int)&o[base].name); + printf("%d,%d,%d,%d\n", (int)&o[base+1].type, (int)&o[base+1].intg, (int)&o[base+1].real, (int)&o[base+1].name); + Principal p, q; + p.x = p.y = q.x = q.y = 0; + p.a.type = A; + p.a.real = 123.456; + *(&q.a) = p.a; + printf("%.2f,%d,%.2f,%.2f : %.2f,%d,%.2f,%.2f\n", p.x, p.a.type, p.a.real, p.y, q.x, q.a.type, q.a.real, q.y); + return 0; + } + ''' + + if self.is_le32(): + self.do_run(src, '''16,32 +0,8,8,8 +16,24,24,24 +0.00,10,123.46,0.00 : 0.00,10,123.46,0.00 +''') + else: + self.do_run(src, '''12,28 +0,4,4,4 +12,16,16,16 +0.00,10,123.46,0.00 : 0.00,10,123.46,0.00 +''') + + def test_unsigned(self): + Settings.CORRECT_SIGNS = 1 # We test for exactly this sort of thing here + Settings.CHECK_SIGNS = 0 + src = ''' + #include + const signed char cvals[2] = { -1, -2 }; // compiler can store this is a string, so -1 becomes \FF, and needs re-signing + int main() + { + { + unsigned char x = 200; + printf("*%d*\\n", x); + unsigned char y = -22; + printf("*%d*\\n", y); + } + + int varey = 100; + unsigned int MAXEY = -1, MAXEY2 = -77; + printf("*%u,%d,%u*\\n", MAXEY, varey >= MAXEY, MAXEY2); // 100 >= -1? not in unsigned! + + int y = cvals[0]; + printf("*%d,%d,%d,%d*\\n", cvals[0], cvals[0] < 0, y, y < 0); + y = cvals[1]; + printf("*%d,%d,%d,%d*\\n", cvals[1], cvals[1] < 0, y, y < 0); + + // zext issue - see mathop in jsifier + unsigned char x8 = -10; + unsigned long hold = 0; + hold += x8; + int y32 = hold+50; + printf("*%u,%u*\\n", hold, y32); + + // Comparisons + x8 = 0; + for (int i = 0; i < 254; i++) x8++; // make it an actual 254 in JS - not a -2 + printf("*%d,%d*\\n", x8+1 == 0xff, x8+1 != 0xff); // 0xff may be '-1' in the bitcode + + return 0; + } + ''' + self.do_run(src, '*4294967295,0,4294967219*\n*-1,1,-1,1*\n*-2,1,-2,1*\n*246,296*\n*1,0*') + + # Now let's see some code that should just work in USE_TYPED_ARRAYS == 2, but requires + # corrections otherwise + if Settings.USE_TYPED_ARRAYS == 2: + Settings.CORRECT_SIGNS = 0 + Settings.CHECK_SIGNS = 1 if not Settings.ASM_JS else 0 + else: + Settings.CORRECT_SIGNS = 1 + Settings.CHECK_SIGNS = 0 + + src = ''' + #include + int main() + { + { + unsigned char x; + unsigned char *y = &x; + *y = -1; + printf("*%d*\\n", x); + } + { + unsigned short x; + unsigned short *y = &x; + *y = -1; + printf("*%d*\\n", x); + } + /*{ // This case is not checked. The hint for unsignedness is just the %u in printf, and we do not analyze that + unsigned int x; + unsigned int *y = &x; + *y = -1; + printf("*%u*\\n", x); + }*/ + { + char x; + char *y = &x; + *y = 255; + printf("*%d*\\n", x); + } + { + char x; + char *y = &x; + *y = 65535; + printf("*%d*\\n", x); + } + { + char x; + char *y = &x; + *y = 0xffffffff; + printf("*%d*\\n", x); + } + return 0; + } + ''' + self.do_run(src, '*255*\n*65535*\n*-1*\n*-1*\n*-1*') + + def test_bitfields(self): + if self.emcc_args is None: Settings.SAFE_HEAP = 0 # bitfields do loads on invalid areas, by design + src = ''' + #include + struct bitty { + unsigned x : 1; + unsigned y : 1; + unsigned z : 1; + }; + int main() + { + bitty b; + printf("*"); + for (int i = 0; i <= 1; i++) + for (int j = 0; j <= 1; j++) + for (int k = 0; k <= 1; k++) { + b.x = i; + b.y = j; + b.z = k; + printf("%d,%d,%d,", b.x, b.y, b.z); + } + printf("*\\n"); + return 0; + } + ''' + self.do_run(src, '*0,0,0,0,0,1,0,1,0,0,1,1,1,0,0,1,0,1,1,1,0,1,1,1,*') + + def test_floatvars(self): + src = ''' + #include + + // headers test, see issue #1013 + #include + #include + + int main(int argc, char **argv) + { + float x = 1.234, y = 3.5, q = 0.00000001; + y *= 3; + int z = x < y; + printf("*%d,%d,%.1f,%d,%.4f,%.2f*\\n", z, int(y), y, (int)x, x, q); + + printf("%.2f, %.2f, %.2f, %.2f\\n", fmin(0.5, 3.3), fmin(NAN, 3.3), fmax(0.5, 3.3), fmax(NAN, 3.3)); + + printf("small: %.10f\\n", argc * 0.000001); + + /* + // Rounding behavior + float fs[6] = { -2.75, -2.50, -2.25, 2.25, 2.50, 2.75 }; + double ds[6] = { -2.75, -2.50, -2.25, 2.25, 2.50, 2.75 }; + for (int i = 0; i < 6; i++) + printf("*int(%.2f)=%d,%d*\\n", fs[i], int(fs[i]), int(ds[i])); + */ + + return 0; + } + ''' + self.do_run(src, '*1,10,10.5,1,1.2340,0.00*\n0.50, 3.30, 3.30, 3.30\nsmall: 0.0000010000\n') + + def test_isnan(self): + src = r''' + #include + + int IsNaN(double x){ + int rc; /* The value return */ + volatile double y = x; + volatile double z = y; + rc = (y!=z); + return rc; + } + + int main() { + double tests[] = { 1.0, 3.333, 1.0/0.0, 0.0/0.0, -1.0/0.0, -0, 0, -123123123, 12.0E200 }; + for (int i = 0; i < sizeof(tests)/sizeof(double); i++) + printf("%d - %f - %d\n", i, tests[i], IsNaN(tests[i])); + } + ''' + self.do_run(src, '''0 - 1.000000 - 0 +1 - 3.333000 - 0 +2 - inf - 0 +3 - nan - 1 +4 - -inf - 0 +5 - 0.000000 - 0 +6 - 0.000000 - 0 +7 - -123123123.000000 - 0 +8 - 1.2e+201 - 0 +''') + + def test_globaldoubles(self): + src = r''' + #include + #include + + double testVu, testVv, testWu, testWv; + + void Test(double _testVu, double _testVv, double _testWu, double _testWv) + { + testVu = _testVu; + testVv = _testVv; + testWu = _testWu; + testWv = _testWv; + printf("BUG?\n"); + printf("Display: Vu=%f Vv=%f Wu=%f Wv=%f\n", testVu, testVv, testWu, testWv); + } + + int main(void) + { + double v1 = 465.1; + double v2 = 465.2; + double v3 = 160.3; + double v4 = 111.4; + Test(v1, v2, v3, v4); + return 0; + } + ''' + self.do_run(src, 'BUG?\nDisplay: Vu=465.100000 Vv=465.200000 Wu=160.300000 Wv=111.400000') + + def test_math(self): + src = ''' + #include + #include + #include + int main() + { + printf("*%.2f,%.2f,%d", M_PI, -M_PI, (1/0.0) > 1e300); // could end up as infinity, or just a very very big number + printf(",%d", isfinite(NAN) != 0); + printf(",%d", isfinite(INFINITY) != 0); + printf(",%d", isfinite(-INFINITY) != 0); + printf(",%d", isfinite(12.3) != 0); + printf(",%d", isinf(NAN) != 0); + printf(",%d", isinf(INFINITY) != 0); + printf(",%d", isinf(-INFINITY) != 0); + printf(",%d", isinf(12.3) != 0); + div_t div_result = div(23, 10); + printf(",%d", div_result.quot); + printf(",%d", div_result.rem); + double sine = -1.0, cosine = -1.0; + sincos(0.0, &sine, &cosine); + printf(",%1.1lf", sine); + printf(",%1.1lf", cosine); + float fsine = -1.0f, fcosine = -1.0f; + sincosf(0.0, &fsine, &fcosine); + printf(",%1.1f", fsine); + printf(",%1.1f", fcosine); + printf("*\\n"); + return 0; + } + ''' + self.do_run(src, '*3.14,-3.14,1,0,0,0,1,0,1,1,0,2,3,0.0,1.0,0.0,1.0*') + + def test_erf(self): + src = ''' + #include + #include + int main() + { + printf("%1.6f, %1.6f, %1.6f, %1.6f, %1.6f, %1.6f\\n", + erf(1.0), + erf(3.0), + erf(-1.0), + erfc(1.0), + erfc(3.0), + erfc(-1.5)); + return 0; + } + ''' + self.do_run(src, '0.842701, 0.999978, -0.842701, 0.157299, 0.000022, 1.966105') + + def test_math_hyperbolic(self): + src = open(path_from_root('tests', 'hyperbolic', 'src.c'), 'r').read() + expected = open(path_from_root('tests', 'hyperbolic', 'output.txt'), 'r').read() + self.do_run(src, expected) + + def test_frexp(self): + src = ''' + #include + #include + #include + + static const double tol=1e-16; + + void test_value(double value) + { + int exponent; + double x=frexp(value, &exponent); + double expected=x*pow(2.0, exponent); + + printf("%f=%f*2^%d\\n", value, x, exponent); + + assert(fabs(expected-value)=5e-1 && fabs(x)<1)); // x has a magnitude in the interval [1/2, 1) + } + + int main() + { + test_value(0); + test_value(100.1); + test_value(-100.1); + test_value(.5); + test_value(-.5); + test_value(1-1e-16); + test_value(-(1-1e-16)); + + return 0; + } + ''' + self.do_run(src, '''0.000000=0.000000*2^0 +100.100000=0.782031*2^7 +-100.100000=-0.782031*2^7 +0.500000=0.500000*2^0 +-0.500000=-0.500000*2^0 +1.000000=1.000000*2^0 +-1.000000=-1.000000*2^0''') + + def test_rounding(self): + src = ''' + #include + #include + + int main() + { + printf("%.1f ", round(1.4)); + printf("%.1f ", round(1.6)); + printf("%.1f ", round(-1.4)); + printf("%.1f ", round(-1.6)); + + printf("%.1f ", round(1.5)); + printf("%.1f ", round(2.5)); + printf("%.1f ", round(-1.5)); + printf("%.1f ", round(-2.5)); + + printf("%ld ", lrint(1.4)); + printf("%ld ", lrint(1.6)); + printf("%ld ", lrint(-1.4)); + printf("%ld ", lrint(-1.6)); + + printf("%ld ", lrint(1.5)); + printf("%ld ", lrint(2.5)); + printf("%ld ", lrint(-1.5)); + printf("%ld ", lrint(-2.5)); + + return 0; + } + ''' + self.do_run(src, "1.0 2.0 -1.0 -2.0 2.0 3.0 -2.0 -3.0 " + "1 2 -1 -2 2 2 -2 -2") + + # This example borrowed from MSDN documentation + def test_fcvt(self): + if self.emcc_args is None: return self.skip('requires emcc') + + src = ''' + #include + #include + + int main() { + int decimal, sign; + char *buffer; + double source = 3.1415926535; + + buffer = fcvt(source, 7, &decimal, &sign); + printf("source: %2.10f buffer: '%s' decimal: %d sign: %d\\n", + source, buffer, decimal, sign); + } + ''' + self.do_run(src, "source: 3.1415926535 buffer: '31415927' decimal: 1 sign: 0"); + + def test_llrint(self): + if Settings.USE_TYPED_ARRAYS != 2: return self.skip('requires ta2') + src = r''' + #include + #include + int main() { + printf("%lld\n%lld\n%lld\n%lld\n", llrint(0.1), llrint(0.6), llrint(1.25), llrint(1099511627776.667)); + return 0; + } + ''' + self.do_run(src, '0\n1\n1\n1099511627777\n') + + def test_getgep(self): + # Generated code includes getelementptr (getelementptr, 0, 1), i.e., GEP as the first param to GEP + src = ''' + #include + struct { + int y[10]; + int z[10]; + } commonblock; + + int main() + { + for (int i = 0; i < 10; ++i) { + commonblock.y[i] = 1; + commonblock.z[i] = 2; + } + printf("*%d %d*\\n", commonblock.y[0], commonblock.z[0]); + return 0; + } + ''' + self.do_run(src, '*1 2*') + + def test_multiply_defined_symbols(self): + a1 = "int f() { return 1; }" + a1_name = os.path.join(self.get_dir(), 'a1.c') + open(a1_name, 'w').write(a1) + a2 = "void x() {}" + a2_name = os.path.join(self.get_dir(), 'a2.c') + open(a2_name, 'w').write(a2) + b1 = "int f() { return 2; }" + b1_name = os.path.join(self.get_dir(), 'b1.c') + open(b1_name, 'w').write(b1) + b2 = "void y() {}" + b2_name = os.path.join(self.get_dir(), 'b2.c') + open(b2_name, 'w').write(b2) + main = r''' + #include + int f(); + int main() { + printf("result: %d\n", f()); + return 0; + } + ''' + main_name = os.path.join(self.get_dir(), 'main.c') + open(main_name, 'w').write(main) + + Building.emcc(a1_name) + Building.emcc(a2_name) + Building.emcc(b1_name) + Building.emcc(b2_name) + Building.emcc(main_name) + + liba_name = os.path.join(self.get_dir(), 'liba.a') + Building.emar('cr', liba_name, [a1_name + '.o', a2_name + '.o']) + libb_name = os.path.join(self.get_dir(), 'libb.a') + Building.emar('cr', libb_name, [b1_name + '.o', b2_name + '.o']) + + all_name = os.path.join(self.get_dir(), 'all.bc') + Building.link([main_name + '.o', liba_name, libb_name], all_name) + + self.do_ll_run(all_name, 'result: 1') + + def test_if(self): + src = ''' + #include + int main() + { + int x = 5; + if (x > 3) { + printf("*yes*\\n"); + } + return 0; + } + ''' + self.do_run(src, '*yes*') + + def test_if_else(self): + src = ''' + #include + int main() + { + int x = 5; + if (x > 10) { + printf("*yes*\\n"); + } else { + printf("*no*\\n"); + } + return 0; + } + ''' + self.do_run(src, '*no*') + + def test_loop(self): + src = ''' + #include + int main() + { + int x = 5; + for (int i = 0; i < 6; i++) { + x += x*i; + if (x > 1000) { + if (x % 7 == 0) printf("cheez\\n"); + x /= 2; + break; + } + } + printf("*%d*\\n", x); + return 0; + } + ''' + + self.do_run(src, '*1800*') + + generated = open('src.cpp.o.js', 'r').read() + + def test_stack(self): + Settings.INLINING_LIMIT = 50 + + src = ''' + #include + int test(int i) { + int x = 10; + if (i > 0) { + return test(i-1); + } + return int(&x); // both for the number, and forces x to not be nativized + } + int main(int argc, char **argv) + { + // We should get the same value for the first and last - stack has unwound + int x1 = test(argc - 2); + int x2 = test(100); + int x3 = test((argc - 2) / 4); + printf("*%d,%d*\\n", x3-x1, x2 != x1); + return 0; + } + ''' + self.do_run(src, '*0,1*') + + def test_strings(self): + src = ''' + #include + #include + #include + + int main(int argc, char **argv) + { + int x = 5, y = 9, magic = 7; // fool compiler with magic + memmove(&x, &y, magic-7); // 0 should not crash us + + int xx, yy, zz; + char s[32]; + int cc = sscanf("abc_10.b1_xyz_543_defg", "abc_%d.%2x_xyz_%3d_%3s", &xx, &yy, &zz, s); + printf("%d:%d,%d,%d,%s\\n", cc, xx, yy, zz, s); + + printf("%d\\n", argc); + puts(argv[1]); + puts(argv[2]); + printf("%d\\n", atoi(argv[3])+2); + const char *foolingthecompiler = "\\rabcd"; + printf("%d\\n", strlen(foolingthecompiler)); // Tests parsing /0D in llvm - should not be a 0 (end string) then a D! + printf("%s\\n", NULL); // Should print '(null)', not the string at address 0, which is a real address for us! + printf("/* a comment */\\n"); // Should not break the generated code! + printf("// another\\n"); // Should not break the generated code! + + char* strdup_val = strdup("test"); + printf("%s\\n", strdup_val); + free(strdup_val); + + { + char *one = "one 1 ONE !"; + char *two = "two 2 TWO ?"; + char three[1024]; + memset(three, '.', 1024); + three[50] = 0; + strncpy(three + argc, one + (argc/2), argc+1); + strncpy(three + argc*3, two + (argc/3), argc+2); + printf("waka %s\\n", three); + } + + { + char *one = "string number one top notch"; + char *two = "fa la sa ho fi FI FO FUM WHEN WHERE WHY HOW WHO"; + char three[1000]; + strcpy(three, &one[argc*2]); + char *four = strcat(three, &two[argc*3]); + printf("cat |%s|\\n", three); + printf("returned |%s|\\n", four); + } + + return 0; + } + ''' + for named in (0, 1): + print named + Settings.NAMED_GLOBALS = named + self.do_run(src, '''4:10,177,543,def\n4\nwowie\ntoo\n76\n5\n(null)\n/* a comment */\n// another\ntest\nwaka ....e 1 O...wo 2 T................................ +cat |umber one top notchfi FI FO FUM WHEN WHERE WHY HOW WHO| +returned |umber one top notchfi FI FO FUM WHEN WHERE WHY HOW WHO|''', ['wowie', 'too', '74']) + if self.emcc_args == []: + gen = open(self.in_dir('src.cpp.o.js')).read() + assert ('var __str1;' in gen) == named + + def test_strcmp_uni(self): + src = ''' + #include + #include + int main() + { + #define TEST(func) \ + { \ + char *word = "WORD"; \ + char wordEntry[2] = { -61,-126 }; /* "Â"; */ \ + int cmp = func(word, wordEntry, 2); \ + printf("Compare value " #func " is %d\\n", cmp); \ + } + TEST(strncmp); + TEST(strncasecmp); + TEST(memcmp); + } + ''' + self.do_run(src, 'Compare value strncmp is -1\nCompare value strncasecmp is -1\nCompare value memcmp is -1\n') + + def test_strndup(self): + src = ''' + //--------------- + //- http://pubs.opengroup.org/onlinepubs/9699919799/functions/strndup.html + //--------------- + + #include + #include + #include + + int main(int argc, char **argv) { + const char* source = "strndup - duplicate a specific number of bytes from a string"; + + char* strdup_val = strndup(source, 0); + printf("1:%s\\n", strdup_val); + free(strdup_val); + + strdup_val = strndup(source, 7); + printf("2:%s\\n", strdup_val); + free(strdup_val); + + strdup_val = strndup(source, 1000); + printf("3:%s\\n", strdup_val); + free(strdup_val); + + strdup_val = strndup(source, 60); + printf("4:%s\\n", strdup_val); + free(strdup_val); + + strdup_val = strndup(source, 19); + printf("5:%s\\n", strdup_val); + free(strdup_val); + + strdup_val = strndup(source, -1); + printf("6:%s\\n", strdup_val); + free(strdup_val); + + return 0; + } + ''' + self.do_run(src, '1:\n2:strndup\n3:strndup - duplicate a specific number of bytes from a string\n4:strndup - duplicate a specific number of bytes from a string\n5:strndup - duplicate\n6:\n') + + def test_errar(self): + src = r''' + #include + #include + #include + + int main() { + char* err; + char buffer[200]; + + err = strerror(EDOM); + strerror_r(EWOULDBLOCK, buffer, 200); + printf("<%s>\n", err); + printf("<%s>\n", buffer); + + printf("<%d>\n", strerror_r(EWOULDBLOCK, buffer, 0)); + errno = 123; + printf("<%d>\n", errno); + + return 0; + } + ''' + expected = ''' + + + <34> + <123> + ''' + self.do_run(src, re.sub('(^|\n)\s+', '\\1', expected)) + + def test_mainenv(self): + src = ''' + #include + int main(int argc, char **argv, char **envp) + { + printf("*%p*\\n", envp); + return 0; + } + ''' + self.do_run(src, '*(nil)*') + + def test_funcs(self): + src = ''' + #include + int funcy(int x) + { + return x*9; + } + int main() + { + printf("*%d,%d*\\n", funcy(8), funcy(10)); + return 0; + } + ''' + self.do_run(src, '*72,90*') + + def test_structs(self): + src = ''' + #include + struct S + { + int x, y; + }; + int main() + { + S a, b; + a.x = 5; a.y = 6; + b.x = 101; b.y = 7009; + S *c, *d; + c = &a; + c->x *= 2; + c = &b; + c->y -= 1; + d = c; + d->y += 10; + printf("*%d,%d,%d,%d,%d,%d,%d,%d*\\n", a.x, a.y, b.x, b.y, c->x, c->y, d->x, d->y); + return 0; + } + ''' + self.do_run(src, '*10,6,101,7018,101,7018,101,7018*') + + gen_struct_src = ''' + #include + #include + #include "emscripten.h" + + struct S + { + int x, y; + }; + int main() + { + S* a = {{gen_struct}}; + a->x = 51; a->y = 62; + printf("*%d,%d*\\n", a->x, a->y); + {{del_struct}}(a); + return 0; + } + ''' + + def test_mallocstruct(self): + self.do_run(self.gen_struct_src.replace('{{gen_struct}}', '(S*)malloc(sizeof(S))').replace('{{del_struct}}', 'free'), '*51,62*') + + def test_newstruct(self): + if self.emcc_args is None: return self.skip('requires emcc') + self.do_run(self.gen_struct_src.replace('{{gen_struct}}', 'new S').replace('{{del_struct}}', 'delete'), '*51,62*') + + def test_addr_of_stacked(self): + src = ''' + #include + void alter(int *y) + { + *y += 5; + } + int main() + { + int x = 2; + alter(&x); + printf("*%d*\\n", x); + return 0; + } + ''' + self.do_run(src, '*7*') + + def test_globals(self): + src = ''' + #include + + char cache[256], *next = cache; + + int main() + { + cache[10] = 25; + next[20] = 51; + printf("*%d,%d*\\n", next[10], cache[20]); + return 0; + } + ''' + self.do_run(src, '*25,51*') + + def test_linked_list(self): + src = ''' + #include + struct worker_args { + int value; + struct worker_args *next; + }; + int main() + { + worker_args a; + worker_args b; + a.value = 60; + a.next = &b; + b.value = 900; + b.next = NULL; + worker_args* c = &a; + int total = 0; + while (c) { + total += c->value; + c = c->next; + } + + // Chunk of em + worker_args chunk[10]; + for (int i = 0; i < 9; i++) { + chunk[i].value = i*10; + chunk[i].next = &chunk[i+1]; + } + chunk[9].value = 90; + chunk[9].next = &chunk[0]; + + c = chunk; + do { + total += c->value; + c = c->next; + } while (c != chunk); + + printf("*%d,%d*\\n", total, b.next); + // NULL *is* 0, in C/C++. No JS null! (null == 0 is false, etc.) + + return 0; + } + ''' + self.do_run(src, '*1410,0*') + + def test_sup(self): + src = ''' + #include + + struct S4 { int x; }; // size: 4 + struct S4_2 { short x, y; }; // size: 4, but for alignment purposes, 2 + struct S6 { short x, y, z; }; // size: 6 + struct S6w { char x[6]; }; // size: 6 also + struct S6z { int x; short y; }; // size: 8, since we align to a multiple of the biggest - 4 + + struct C___ { S6 a, b, c; int later; }; + struct Carr { S6 a[3]; int later; }; // essentially the same, but differently defined + struct C__w { S6 a; S6w b; S6 c; int later; }; // same size, different struct + struct Cp1_ { int pre; short a; S6 b, c; int later; }; // fillers for a + struct Cp2_ { int a; short pre; S6 b, c; int later; }; // fillers for a (get addr of the other filler) + struct Cint { S6 a; int b; S6 c; int later; }; // An int (different size) for b + struct C4__ { S6 a; S4 b; S6 c; int later; }; // Same size as int from before, but a struct + struct C4_2 { S6 a; S4_2 b; S6 c; int later; }; // Same size as int from before, but a struct with max element size 2 + struct C__z { S6 a; S6z b; S6 c; int later; }; // different size, 8 instead of 6 + + int main() + { + #define TEST(struc) \\ + { \\ + struc *s = 0; \\ + printf("*%s: %d,%d,%d,%d<%d*\\n", #struc, (int)&(s->a), (int)&(s->b), (int)&(s->c), (int)&(s->later), sizeof(struc)); \\ + } + #define TEST_ARR(struc) \\ + { \\ + struc *s = 0; \\ + printf("*%s: %d,%d,%d,%d<%d*\\n", #struc, (int)&(s->a[0]), (int)&(s->a[1]), (int)&(s->a[2]), (int)&(s->later), sizeof(struc)); \\ + } + printf("sizeofs:%d,%d\\n", sizeof(S6), sizeof(S6z)); + TEST(C___); + TEST_ARR(Carr); + TEST(C__w); + TEST(Cp1_); + TEST(Cp2_); + TEST(Cint); + TEST(C4__); + TEST(C4_2); + TEST(C__z); + return 0; + } + ''' + if Settings.QUANTUM_SIZE == 1: + self.do_run(src, 'sizeofs:6,8\n*C___: 0,3,6,9<24*\n*Carr: 0,3,6,9<24*\n*C__w: 0,3,9,12<24*\n*Cp1_: 1,2,5,8<24*\n*Cp2_: 0,2,5,8<24*\n*Cint: 0,3,4,7<24*\n*C4__: 0,3,4,7<24*\n*C4_2: 0,3,5,8<20*\n*C__z: 0,3,5,8<28*') + else: + self.do_run(src, 'sizeofs:6,8\n*C___: 0,6,12,20<24*\n*Carr: 0,6,12,20<24*\n*C__w: 0,6,12,20<24*\n*Cp1_: 4,6,12,20<24*\n*Cp2_: 0,6,12,20<24*\n*Cint: 0,8,12,20<24*\n*C4__: 0,8,12,20<24*\n*C4_2: 0,6,10,16<20*\n*C__z: 0,8,16,24<28*') + + def test_assert(self): + src = ''' + #include + #include + int main() { + assert(1 == true); // pass + assert(1 == false); // fail + return 0; + } + ''' + self.do_run(src, 'Assertion failed: 1 == false') + + def test_libcextra(self): + if self.emcc_args is None: return self.skip('needs emcc for libcextra') + src = r''' + #include + #include + + int main() + { + const wchar_t* wstr = L"Hello"; + + printf("wcslen: %d\n", wcslen(wstr)); + + return 0; + } + ''' + self.do_run(src, 'wcslen: 5') + + def test_longjmp(self): + src = r''' + #include + #include + + static jmp_buf buf; + + void second(void) { + printf("second\n"); + longjmp(buf,-1); + } + + void first(void) { + printf("first\n"); // prints + longjmp(buf,1); // jumps back to where setjmp was called - making setjmp now return 1 + } + + int main() { + volatile int x = 0; + int jmpval = setjmp(buf); + if (!jmpval) { + x++; // should be properly restored once longjmp jumps back + first(); // when executed, setjmp returns 1 + printf("skipped\n"); // does not print + } else if (jmpval == 1) { // when first() jumps back, setjmp returns 1 + printf("result: %d %d\n", x, jmpval); // prints + x++; + second(); // when executed, setjmp returns -1 + } else if (jmpval == -1) { // when second() jumps back, setjmp returns -1 + printf("result: %d %d\n", x, jmpval); // prints + } + + return 0; + } + ''' + self.do_run(src, 'first\nresult: 1 1\nsecond\nresult: 2 -1') + + def test_longjmp2(self): + src = r''' + #include + #include + + typedef struct { + jmp_buf* jmp; + } jmp_state; + + void stack_manipulate_func(jmp_state* s, int level) { + jmp_buf buf; + + printf("Entering stack_manipulate_func, level: %d\n", level); + + if (level == 0) { + s->jmp = &buf; + if (setjmp(*(s->jmp)) == 0) { + printf("Setjmp normal execution path, level: %d\n", level); + stack_manipulate_func(s, level + 1); + } else { + printf("Setjmp error execution path, level: %d\n", level); + } + } else { + printf("Perform longjmp at level %d\n", level); + longjmp(*(s->jmp), 1); + } + + printf("Exiting stack_manipulate_func, level: %d\n", level); + } + + int main(int argc, char *argv[]) { + jmp_state s; + s.jmp = NULL; + stack_manipulate_func(&s, 0); + + return 0; + } + ''' + self.do_run(src, '''Entering stack_manipulate_func, level: 0 +Setjmp normal execution path, level: 0 +Entering stack_manipulate_func, level: 1 +Perform longjmp at level 1 +Setjmp error execution path, level: 0 +Exiting stack_manipulate_func, level: 0 +''') + + def test_longjmp3(self): + src = r''' + #include + #include + + typedef struct { + jmp_buf* jmp; + } jmp_state; + + void setjmp_func(jmp_state* s, int level) { + jmp_buf* prev_jmp = s->jmp; + jmp_buf c_jmp; + + if (level == 2) { + printf("level is 2, perform longjmp!\n"); + longjmp(*(s->jmp), 1); + } + + if (setjmp(c_jmp) == 0) { + printf("setjmp normal execution path, level: %d\n", level); + s->jmp = &c_jmp; + setjmp_func(s, level + 1); + } else { + printf("setjmp exception execution path, level: %d\n", level); + if (prev_jmp) { + printf("prev_jmp is not empty, continue with longjmp!\n"); + s->jmp = prev_jmp; + longjmp(*(s->jmp), 1); + } + } + + printf("Exiting setjmp function, level: %d\n", level); + } + + int main(int argc, char *argv[]) { + jmp_state s; + s.jmp = NULL; + + setjmp_func(&s, 0); + + return 0; + } + ''' + self.do_run(src, '''setjmp normal execution path, level: 0 +setjmp normal execution path, level: 1 +level is 2, perform longjmp! +setjmp exception execution path, level: 1 +prev_jmp is not empty, continue with longjmp! +setjmp exception execution path, level: 0 +Exiting setjmp function, level: 0 +''') + + def test_longjmp4(self): + src = r''' + #include + #include + + typedef struct { + jmp_buf* jmp; + } jmp_state; + + void second_func(jmp_state* s); + + void first_func(jmp_state* s) { + jmp_buf* prev_jmp = s->jmp; + jmp_buf c_jmp; + volatile int once = 0; + + if (setjmp(c_jmp) == 0) { + printf("Normal execution path of first function!\n"); + + s->jmp = &c_jmp; + second_func(s); + } else { + printf("Exception execution path of first function! %d\n", once); + + if (!once) { + printf("Calling longjmp the second time!\n"); + once = 1; + longjmp(*(s->jmp), 1); + } + } + } + + void second_func(jmp_state* s) { + longjmp(*(s->jmp), 1); + } + + int main(int argc, char *argv[]) { + jmp_state s; + s.jmp = NULL; + + first_func(&s); + + return 0; + } + ''' + self.do_run(src, '''Normal execution path of first function! +Exception execution path of first function! 0 +Calling longjmp the second time! +Exception execution path of first function! 1 +''') + + def test_longjmp_funcptr(self): + src = r''' + #include + #include + + static jmp_buf buf; + + void (*fp)() = NULL; + + void second(void) { + printf("second\n"); // prints + longjmp(buf,1); // jumps back to where setjmp was called - making setjmp now return 1 + } + + void first(void) { + fp(); + printf("first\n"); // does not print + } + + int main(int argc, char **argv) { + fp = argc == 200 ? NULL : second; + + volatile int x = 0; + if ( ! setjmp(buf) ) { + x++; + first(); // when executed, setjmp returns 0 + } else { // when longjmp jumps back, setjmp returns 1 + printf("main: %d\n", x); // prints + } + + return 0; + } + ''' + self.do_run(src, 'second\nmain: 1\n') + + def test_longjmp_repeat(self): + Settings.MAX_SETJMPS = 1 + + src = r''' + #include + #include + + static jmp_buf buf; + + int main() { + volatile int x = 0; + printf("setjmp:%d\n", setjmp(buf)); + x++; + printf("x:%d\n", x); + if (x < 4) longjmp(buf, x*2); + return 0; + } + ''' + self.do_run(src, '''setjmp:0 +x:1 +setjmp:2 +x:2 +setjmp:4 +x:3 +setjmp:6 +x:4 +''') + + def test_longjmp_stacked(self): + src = r''' + #include + #include + #include + #include + + int bottom, top; + + int run(int y) { + // confuse stack + char *s = (char*)alloca(100); + memset(s, 1, 100); + s[y] = y; + s[y/2] = y*2; + volatile int x = s[y]; + top = (int)alloca(4); + if (x <= 2) return x; + jmp_buf buf; + printf("setjmp of %d\n", x); + if (setjmp(buf) == 0) { + printf("going\n"); + x += run(x/2); + longjmp(buf, 1); + } + printf("back\n"); + return x/2; + } + + int main(int argc, char **argv) { + int sum = 0; + for (int i = 0; i < argc*2; i++) { + bottom = (int)alloca(4); + sum += run(10); + // scorch the earth + if (bottom < top) { + memset((void*)bottom, 1, top - bottom); + } else { + memset((void*)top, 1, bottom - top); + } + } + printf("%d\n", sum); + return sum; + } + ''' + self.do_run(src, '''setjmp of 10 +going +setjmp of 5 +going +back +back +setjmp of 10 +going +setjmp of 5 +going +back +back +12 +''') + + def test_longjmp_exc(self): + src = r''' + #include + #include + #include + #include + + jmp_buf abortframe; + + void dostuff(int a) { + printf("pre\n"); + if (a != 42) emscripten_run_script("waka_waka()"); // this should fail, and never reach "never" + printf("never\n"); + + if (a == 100) { + longjmp (abortframe, -1); + } + + if (setjmp(abortframe)) { + printf("got 100"); + } + } + + int main(int argc, char **argv) { + dostuff(argc); + exit(1); + return 1; + } + ''' + self.do_run(src, 'waka_waka'); + + def test_setjmp_many(self): + src = r''' + #include + #include + + int main(int argc) { + jmp_buf buf; + for (int i = 0; i < NUM; i++) printf("%d\n", setjmp(buf)); + if (argc-- == 1131) longjmp(buf, 11); + return 0; + } + ''' + for num in [Settings.MAX_SETJMPS, Settings.MAX_SETJMPS+1]: + print num + self.do_run(src.replace('NUM', str(num)), '0\n' * num if num <= Settings.MAX_SETJMPS or not Settings.ASM_JS else 'build with a higher value for MAX_SETJMPS') + + def test_exceptions(self): + if Settings.QUANTUM_SIZE == 1: return self.skip("we don't support libcxx in q1") + if self.emcc_args is None: return self.skip('need emcc to add in libcxx properly') + + Settings.EXCEPTION_DEBUG = 1 + + Settings.DISABLE_EXCEPTION_CATCHING = 0 + if '-O2' in self.emcc_args: + self.emcc_args += ['--closure', '1'] # Use closure here for some additional coverage + + src = ''' + #include + void thrower() { + printf("infunc..."); + throw(99); + printf("FAIL"); + } + int main() { + try { + printf("*throw..."); + throw(1); + printf("FAIL"); + } catch(...) { + printf("caught!"); + } + try { + thrower(); + } catch(...) { + printf("done!*\\n"); + } + return 0; + } + ''' + self.do_run(src, '*throw...caught!infunc...done!*') + + Settings.DISABLE_EXCEPTION_CATCHING = 1 + self.do_run(src, 'Exception catching is disabled, this exception cannot be caught. Compile with -s DISABLE_EXCEPTION_CATCHING=0') + + src = ''' + #include + + class MyException + { + public: + MyException(){ std::cout << "Construct..."; } + MyException( const MyException & ) { std::cout << "Copy..."; } + ~MyException(){ std::cout << "Destruct..."; } + }; + + int function() + { + std::cout << "Throw..."; + throw MyException(); + } + + int function2() + { + return function(); + } + + int main() + { + try + { + function2(); + } + catch (MyException & e) + { + std::cout << "Catched..."; + } + + try + { + function2(); + } + catch (MyException e) + { + std::cout << "Catched..."; + } + + return 0; + } + ''' + + Settings.DISABLE_EXCEPTION_CATCHING = 0 + if '-O2' in self.emcc_args: + self.emcc_args.pop() ; self.emcc_args.pop() # disable closure to work around a closure bug + self.do_run(src, 'Throw...Construct...Catched...Destruct...Throw...Construct...Copy...Catched...Destruct...Destruct...') + + def test_exception_2(self): + if self.emcc_args is None: return self.skip('need emcc to add in libcxx properly') + Settings.DISABLE_EXCEPTION_CATCHING = 0 + src = r''' + #include + #include + + typedef void (*FuncPtr)(); + + void ThrowException() + { + throw std::runtime_error("catch me!"); + } + + FuncPtr ptr = ThrowException; + + int main() + { + try + { + ptr(); + } + catch(...) + { + printf("Exception caught successfully!\n"); + } + return 0; + } + ''' + self.do_run(src, 'Exception caught successfully!') + + def test_white_list_exception(self): + Settings.DISABLE_EXCEPTION_CATCHING = 2 + Settings.EXCEPTION_CATCHING_WHITELIST = ["__Z12somefunctionv"] + Settings.INLINING_LIMIT = 50 # otherwise it is inlined and not identified + + src = ''' + #include + + void thrower() { + printf("infunc..."); + throw(99); + printf("FAIL"); + } + + void somefunction() { + try { + thrower(); + } catch(...) { + printf("done!*\\n"); + } + } + + int main() { + somefunction(); + return 0; + } + ''' + self.do_run(src, 'infunc...done!*') + + Settings.DISABLE_EXCEPTION_CATCHING = 0 + Settings.EXCEPTION_CATCHING_WHITELIST = [] + + def test_uncaught_exception(self): + if self.emcc_args is None: return self.skip('no libcxx inclusion without emcc') + + Settings.DISABLE_EXCEPTION_CATCHING = 0 + + src = r''' + #include + #include + struct X { + ~X() { + printf("exception? %s\n", std::uncaught_exception() ? "yes" : "no"); + } + }; + int main() { + printf("exception? %s\n", std::uncaught_exception() ? "yes" : "no"); + try { + X x; + throw 1; + } catch(...) { + printf("exception? %s\n", std::uncaught_exception() ? "yes" : "no"); + } + printf("exception? %s\n", std::uncaught_exception() ? "yes" : "no"); + return 0; + } + ''' + self.do_run(src, 'exception? no\nexception? yes\nexception? no\nexception? no\n') + + src = r''' + #include + #include + int main() { + std::ofstream os("test"); + os << std::unitbuf << "foo"; // trigger a call to std::uncaught_exception from + // std::basic_ostream::sentry::~sentry + std::cout << "success"; + } + ''' + self.do_run(src, 'success') + + def test_typed_exceptions(self): + Settings.DISABLE_EXCEPTION_CATCHING = 0 + Settings.SAFE_HEAP = 0 # Throwing null will cause an ignorable null pointer access. + src = open(path_from_root('tests', 'exceptions', 'typed.cpp'), 'r').read() + expected = open(path_from_root('tests', 'exceptions', 'output.txt'), 'r').read() + self.do_run(src, expected) + + def test_multiexception(self): + Settings.DISABLE_EXCEPTION_CATCHING = 0 + src = r''' +#include + +static int current_exception_id = 0; + +typedef struct { +int jmp; +} jmp_state; + +void setjmp_func(jmp_state* s, int level) { +int prev_jmp = s->jmp; +int c_jmp; + +if (level == 2) { + printf("level is 2, perform longjmp!\n"); + throw 1; +} + +c_jmp = current_exception_id++; +try { + printf("setjmp normal execution path, level: %d, prev_jmp: %d\n", level, prev_jmp); + s->jmp = c_jmp; + setjmp_func(s, level + 1); +} catch (int catched_eid) { + printf("caught %d\n", catched_eid); + if (catched_eid == c_jmp) { + printf("setjmp exception execution path, level: %d, prev_jmp: %d\n", level, prev_jmp); + if (prev_jmp != -1) { + printf("prev_jmp is not empty, continue with longjmp!\n"); + s->jmp = prev_jmp; + throw s->jmp; + } + } else { + throw; + } +} + +printf("Exiting setjmp function, level: %d, prev_jmp: %d\n", level, prev_jmp); +} + +int main(int argc, char *argv[]) { +jmp_state s; +s.jmp = -1; + +setjmp_func(&s, 0); + +return 0; +} +''' + self.do_run(src, '''setjmp normal execution path, level: 0, prev_jmp: -1 +setjmp normal execution path, level: 1, prev_jmp: 0 +level is 2, perform longjmp! +caught 1 +setjmp exception execution path, level: 1, prev_jmp: 0 +prev_jmp is not empty, continue with longjmp! +caught 0 +setjmp exception execution path, level: 0, prev_jmp: -1 +Exiting setjmp function, level: 0, prev_jmp: -1 +''') + + def test_std_exception(self): + if self.emcc_args is None: return self.skip('requires emcc') + Settings.DISABLE_EXCEPTION_CATCHING = 0 + self.emcc_args += ['-s', 'SAFE_HEAP=0'] + + src = r''' + #include + #include + + int main() + { + std::exception e; + try { + throw e; + } catch(std::exception e) { + printf("caught std::exception\n"); + } + return 0; + } + ''' + self.do_run(src, 'caught std::exception') + + def test_async_exit(self): + open('main.c', 'w').write(r''' + #include + #include + #include "emscripten.h" + + void main_loop() { + exit(EXIT_SUCCESS); + } + + int main() { + emscripten_set_main_loop(main_loop, 60, 0); + return 0; + } + ''') + + Popen([PYTHON, EMCC, 'main.c']).communicate() + self.assertNotContained('Reached an unreachable!', run_js(self.in_dir('a.out.js'), stderr=STDOUT)) + + def test_exit_stack(self): + if self.emcc_args is None: return self.skip('requires emcc') + if Settings.ASM_JS: return self.skip('uses report_stack without exporting') + + Settings.INLINING_LIMIT = 50 + + src = r''' + #include + #include + + extern "C" { + extern void report_stack(int x); + } + + char moar() { + char temp[125]; + for (int i = 0; i < 125; i++) temp[i] = i*i; + for (int i = 1; i < 125; i++) temp[i] += temp[i-1]/2; + if (temp[100] != 99) exit(1); + return temp[120]; + } + + int main(int argc, char *argv[]) { + report_stack((int)alloca(4)); + printf("*%d*\n", moar()); + return 0; + } + ''' + + open(os.path.join(self.get_dir(), 'pre.js'), 'w').write(''' + var initialStack = -1; + var _report_stack = function(x) { + Module.print('reported'); + initialStack = x; + } + var Module = { + postRun: function() { + Module.print('Exit Status: ' + EXITSTATUS); + Module.print('postRun'); + assert(initialStack == STACKTOP, [initialStack, STACKTOP]); + Module.print('ok.'); + } + }; + ''') + + self.emcc_args += ['--pre-js', 'pre.js'] + self.do_run(src, '''reported\nexit(1) called\nExit Status: 1\npostRun\nok.\n''') + + def test_class(self): + src = ''' + #include + struct Random { + enum { IM = 139968, IA = 3877, IC = 29573 }; + Random() : last(42) {} + float get( float max = 1.0f ) { + last = ( last * IA + IC ) % IM; + return max * last / IM; + } + protected: + unsigned int last; + } rng1; + int main() + { + Random rng2; + int count = 0; + for (int i = 0; i < 100; i++) { + float x1 = rng1.get(); + float x2 = rng2.get(); + printf("%f, %f\\n", x1, x2); + if (x1 != x2) count += 1; + } + printf("*%d*\\n", count); + return 0; + } + ''' + self.do_run(src, '*0*') + + def test_inherit(self): + src = ''' + #include + struct Parent { + int x1, x2; + }; + struct Child : Parent { + int y; + }; + int main() + { + Parent a; + a.x1 = 50; + a.x2 = 87; + Child b; + b.x1 = 78; + b.x2 = 550; + b.y = 101; + Child* c = (Child*)&a; + c->x1 ++; + c = &b; + c->y --; + printf("*%d,%d,%d,%d,%d,%d,%d*\\n", a.x1, a.x2, b.x1, b.x2, b.y, c->x1, c->x2); + return 0; + } + ''' + self.do_run(src, '*51,87,78,550,100,78,550*') + + def test_isdigit_l(self): + if self.emcc_args is None: return self.skip('no libcxx inclusion without emcc') + + src = ''' + #include + int main() { + using namespace std; + use_facet >(cout.getloc()).put(cout, cout, '0', 3.14159265); + } + ''' + self.do_run(src, '3.14159') + + def test_polymorph(self): + if self.emcc_args is None: return self.skip('requires emcc') + src = ''' + #include + struct Pure { + virtual int implme() = 0; + }; + struct Parent : Pure { + virtual int getit() { return 11; }; + int implme() { return 32; } + }; + struct Child : Parent { + int getit() { return 74; } + int implme() { return 1012; } + }; + + struct Other { + int one() { return 11; } + int two() { return 22; } + }; + + int main() + { + Parent *x = new Parent(); + Parent *y = new Child(); + printf("*%d,%d,%d,%d*\\n", x->getit(), y->getit(), x->implme(), y->implme()); + + Other *o = new Other; + int (Other::*Ls)() = &Other::one; + printf("*%d*\\n", (o->*(Ls))()); + Ls = &Other::two; + printf("*%d*\\n", (o->*(Ls))()); + + return 0; + } + ''' + self.do_run(src, '*11,74,32,1012*\n*11*\n*22*') + + def test_segfault(self): + if self.emcc_args is None: return self.skip('SAFE_HEAP without ta2 means we check types too, which hide segfaults') + if Settings.ASM_JS: return self.skip('asm does not support safe heap') + + Settings.SAFE_HEAP = 1 + + for addr in ['0', 'new D2()']: + print addr + src = r''' + #include + + struct Classey { + virtual void doIt() = 0; + }; + + struct D1 : Classey { + virtual void doIt() { printf("fleefl\n"); } + }; + + struct D2 : Classey { + virtual void doIt() { printf("marfoosh\n"); } + }; + + int main(int argc, char **argv) + { + Classey *p = argc == 100 ? new D1() : (Classey*)%s; + + p->doIt(); + + return 0; + } + ''' % addr + self.do_run(src, 'segmentation fault' if addr.isdigit() else 'marfoosh') + + def test_safe_dyncalls(self): + if Settings.ASM_JS: return self.skip('asm does not support missing function stack traces') + if Settings.SAFE_HEAP: return self.skip('safe heap warning will appear instead') + if self.emcc_args is None: return self.skip('need libc') + + Settings.SAFE_DYNCALLS = 1 + + for cond, body, work in [(True, True, False), (True, False, False), (False, True, True), (False, False, False)]: + print cond, body, work + src = r''' + #include + + struct Classey { + virtual void doIt() = 0; + }; + + struct D1 : Classey { + virtual void doIt() BODY; + }; + + int main(int argc, char **argv) + { + Classey *p = argc COND 100 ? new D1() : NULL; + printf("%p\n", p); + p->doIt(); + + return 0; + } + '''.replace('COND', '==' if cond else '!=').replace('BODY', r'{ printf("all good\n"); }' if body else '') + self.do_run(src, 'dyncall error: vi' if not work else 'all good') + + def test_dynamic_cast(self): + if self.emcc_args is None: return self.skip('need libcxxabi') + + src = r''' + #include + + struct Support { + virtual void f() { + printf("f()\n"); + } + }; + + struct Derived : Support { + }; + + int main() { + Support * p = new Derived; + dynamic_cast(p)->f(); + } + ''' + self.do_run(src, 'f()\n') + + def test_dynamic_cast_b(self): + if self.emcc_args is None: return self.skip('need libcxxabi') + + src = ''' + #include + + class CBase { virtual void dummy() {} }; + class CDerived : public CBase { int a; }; + class CDerivedest : public CDerived { float b; }; + + int main () + { + CBase *pa = new CBase; + CBase *pb = new CDerived; + CBase *pc = new CDerivedest; + + printf("a1: %d\\n", dynamic_cast(pa) != NULL); + printf("a2: %d\\n", dynamic_cast(pa) != NULL); + printf("a3: %d\\n", dynamic_cast(pa) != NULL); + + printf("b1: %d\\n", dynamic_cast(pb) != NULL); + printf("b2: %d\\n", dynamic_cast(pb) != NULL); + printf("b3: %d\\n", dynamic_cast(pb) != NULL); + + printf("c1: %d\\n", dynamic_cast(pc) != NULL); + printf("c2: %d\\n", dynamic_cast(pc) != NULL); + printf("c3: %d\\n", dynamic_cast(pc) != NULL); + + return 0; + } + ''' + self.do_run(src, 'a1: 0\na2: 0\na3: 1\nb1: 0\nb2: 1\nb3: 1\nc1: 1\nc2: 1\nc3: 1\n') + + def test_dynamic_cast_2(self): + if self.emcc_args is None: return self.skip('need libcxxabi') + + src = r''' + #include + #include + + class Class {}; + + int main() { + const Class* dp = dynamic_cast(&typeid(Class)); + // should return dp == NULL, + printf("pointer: %p\n", dp); + } + ''' + self.do_run(src, "pointer: (nil)") + + def test_funcptr(self): + src = ''' + #include + int calc1() { return 26; } + int calc2() { return 90; } + typedef int (*fp_t)(); + + fp_t globally1 = calc1; + fp_t globally2 = calc2; + + int nothing(const char *str) { return 0; } + + int main() + { + fp_t fp = calc1; + void *vp = (void*)fp; + fp_t fpb = (fp_t)vp; + fp_t fp2 = calc2; + void *vp2 = (void*)fp2; + fp_t fpb2 = (fp_t)vp2; + printf("*%d,%d,%d,%d,%d,%d*\\n", fp(), fpb(), fp2(), fpb2(), globally1(), globally2()); + + fp_t t = calc1; + printf("*%d,%d", t == calc1, t == calc2); + t = calc2; + printf(",%d,%d*\\n", t == calc1, t == calc2); + + int (*other)(const char *str); + other = nothing; + other("*hello!*"); + other = puts; + other("*goodbye!*"); + + return 0; + } + ''' + self.do_run(src, '*26,26,90,90,26,90*\n*1,0,0,1*\n*goodbye!*') + + def test_mathfuncptr(self): + src = ''' + #include + #include + + int + main(int argc, char **argv) { + float (*fn)(float) = argc != 12 ? &sqrtf : &fabsf; + float (*fn2)(float) = argc != 13 ? &fabsf : &sqrtf; + float (*fn3)(float) = argc != 14 ? &erff : &fabsf; + printf("fn2(-5) = %d, fn(10) = %.2f, erf(10) = %.2f\\n", (int)fn2(-5), fn(10), fn3(10)); + return 0; + } + ''' + self.do_run(src, 'fn2(-5) = 5, fn(10) = 3.16, erf(10) = 1.00') + + def test_funcptrfunc(self): + src = r''' + #include + + typedef void (*funcptr)(int, int); + typedef funcptr (*funcptrfunc)(int); + + funcptr __attribute__ ((noinline)) getIt(int x) { + return (funcptr)x; + } + + int main(int argc, char **argv) + { + funcptrfunc fpf = argc < 100 ? getIt : NULL; + printf("*%p*\n", fpf(argc)); + return 0; + } + ''' + self.do_run(src, '*0x1*') + + def test_funcptr_namecollide(self): + src = r''' + #include + + void do_call(void (*puts)(const char *), const char *str); + + void do_print(const char *str) { + if (!str) do_call(NULL, "delusion"); + if ((int)str == -1) do_print(str+10); + puts("===="); + puts(str); + puts("===="); + } + + void do_call(void (*puts)(const char *), const char *str) { + if (!str) do_print("confusion"); + if ((int)str == -1) do_call(NULL, str-10); + (*puts)(str); + } + + int main(int argc, char **argv) + { + for (int i = 0; i < argc; i++) { + do_call(i != 10 ? do_print : NULL, i != 15 ? "waka waka" : NULL); + } + return 0; + } + ''' + self.do_run(src, 'waka', force_c=True) + + def test_emptyclass(self): + if self.emcc_args is None: return self.skip('requires emcc') + src = ''' + #include + + struct Randomized { + Randomized(int x) { + printf("*zzcheezzz*\\n"); + } + }; + + int main( int argc, const char *argv[] ) { + new Randomized(55); + + return 0; + } + ''' + self.do_run(src, '*zzcheezzz*') + + def test_alloca(self): + src = ''' + #include + #include + + int main() { + char *pc; + pc = (char *)alloca(5); + printf("z:%d*%d*\\n", pc > 0, (int)pc); + return 0; + } + ''' + self.do_run(src, 'z:1*', force_c=True) + + def test_rename(self): + src = open(path_from_root('tests', 'stdio', 'test_rename.c'), 'r').read() + self.do_run(src, 'success', force_c=True) + + def test_alloca_stack(self): + if self.emcc_args is None: return # too slow in other modes + + # We should not blow up the stack with numerous allocas + src = ''' + #include + #include + + func(int i) { + char *pc = (char *)alloca(100); + *pc = i; + (*pc)++; + return (*pc) % 10; + } + int main() { + int total = 0; + for (int i = 0; i < 1024*1024; i++) + total += func(i); + printf("ok:%d*\\n", total); + return 0; + } + ''' + self.do_run(src, 'ok:-32768*', force_c=True) + + def test_stack_byval(self): + if self.emcc_args is None: return # too slow in other modes + + # We should also not blow up the stack with byval arguments + src = r''' + #include + struct vec { + int x, y, z; + vec(int x_, int y_, int z_) : x(x_), y(y_), z(z_) {} + static vec add(vec a, vec b) { + return vec(a.x+b.x, a.y+b.y, a.z+b.z); + } + }; + int main() { + int total = 0; + for (int i = 0; i < 1000; i++) { + for (int j = 0; j < 1000; j++) { + vec c(i+i%10, j*2, i%255); + vec d(j*2, j%255, i%120); + vec f = vec::add(c, d); + total += (f.x + f.y + f.z) % 100; + total %= 10240; + } + } + printf("sum:%d*\n", total); + return 0; + } + ''' + self.do_run(src, 'sum:9780*') + + def test_stack_varargs(self): + if self.emcc_args is None: return # too slow in other modes + + Settings.INLINING_LIMIT = 50 + + # We should not blow up the stack with numerous varargs + src = r''' + #include + #include + + 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_stack_varargs2(self): + if self.emcc_args is None: return # too slow in other modes + Settings.TOTAL_STACK = 1024 + src = r''' + #include + #include + + void func(int i) { + } + int main() { + for (int i = 0; i < 1024; 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); + } + printf("ok!\n"); + return 0; + } + ''' + self.do_run(src, 'ok!') + + print 'with return' + + src = r''' + #include + #include + + int main() { + for (int i = 0; i < 1024; i++) { + int j = 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", + 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); + printf(" (%d)\n", j); + } + printf("ok!\n"); + return 0; + } + ''' + self.do_run(src, 'ok!') + + print 'with definitely no return' + + src = r''' + #include + #include + #include + + void vary(const char *s, ...) + { + va_list v; + va_start(v, s); + char d[20]; + vsnprintf(d, 20, s, v); + puts(d); + + // Try it with copying + va_list tempva; + va_copy(tempva, v); + vsnprintf(d, 20, s, tempva); + puts(d); + + va_end(v); + } + + int main() { + for (int i = 0; i < 1024; i++) { + int j = 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", + 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); + printf(" (%d)\n", j); + vary("*cheez: %d+%d*", 99, 24); + vary("*albeit*"); + } + printf("ok!\n"); + return 0; + } + ''' + self.do_run(src, 'ok!') + + def test_stack_void(self): + Settings.INLINING_LIMIT = 50 + + src = r''' + #include + + static char s[100]="aaaaa"; + static int func(void) { + if(s[0]!='a') return 0; + printf("iso open %s\n", s, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001, 1.001); + return 0; + } + int main(){ + int i; + for(i=0;i<5000;i++) + func(); + printf(".ok.\n"); + } + ''' + self.do_run(src, '.ok.\n') + + def test_life(self): + if self.emcc_args is None: return self.skip('need c99') + self.emcc_args += ['-std=c99'] + src = open(path_from_root('tests', 'life.c'), 'r').read() + self.do_run(src, '''-------------------------------- +[] [] [][][] + [] [] [] [][] [] [] [] +[] [][] [][] [][][] [] + [] [] [] [] [][] [] [] + [] [][] [] [] [] [] [][][][] + [][] [][] [] [][][] [] [] + [] [][] [][] [][] [][][] + [][] [][][] [] [] + [][] [][] [] + [][][] + [] + + + + + [][][] + [] [][] [][] + [][] [] [][] [][] + [][] [][] + [] + [][] + [][] [] +[] [][] [] + [][][] [] + [] [][] +[] [] [] + [] +[] [] [] + [][][] + + [] + [][][] [] +-------------------------------- +''', ['2'], force_c=True) + + def test_array2(self): + src = ''' + #include + + static const double grid[4][2] = { + {-3/3.,-1/3.},{+1/3.,-3/3.}, + {-1/3.,+3/3.},{+3/3.,+1/3.} + }; + + int main() { + for (int i = 0; i < 4; i++) + printf("%d:%.2f,%.2f ", i, grid[i][0], grid[i][1]); + printf("\\n"); + return 0; + } + ''' + self.do_run(src, '0:-1.00,-0.33 1:0.33,-1.00 2:-0.33,1.00 3:1.00,0.33') + + def test_array2b(self): + src = ''' + #include + + static const struct { + unsigned char left; + unsigned char right; + } prioritah[] = { + {6, 6}, {6, 6}, {7, 95}, {7, 7} + }; + + int main() { + printf("*%d,%d\\n", prioritah[1].left, prioritah[1].right); + printf("%d,%d*\\n", prioritah[2].left, prioritah[2].right); + return 0; + } + ''' + self.do_run(src, '*6,6\n7,95*') + + + def test_constglobalstructs(self): + src = ''' + #include + struct IUB { + int c; + double p; + unsigned int pi; + }; + + IUB iub[] = { + { 'a', 0.27, 5 }, + { 'c', 0.15, 4 }, + { 'g', 0.12, 3 }, + { 't', 0.27, 2 }, + }; + + const unsigned char faceedgesidx[6][4] = + { + { 4, 5, 8, 10 }, + { 6, 7, 9, 11 }, + { 0, 2, 8, 9 }, + { 1, 3, 10,11 }, + { 0, 1, 4, 6 }, + { 2, 3, 5, 7 }, + }; + + int main( int argc, const char *argv[] ) { + printf("*%d,%d,%d,%d*\\n", iub[0].c, int(iub[1].p*100), iub[2].pi, faceedgesidx[3][2]); + return 0; + } + ''' + self.do_run(src, '*97,15,3,10*') + + def test_conststructs(self): + src = ''' + #include + struct IUB { + int c; + double p; + unsigned int pi; + }; + + int main( int argc, const char *argv[] ) { + int before = 70; + IUB iub[] = { + { 'a', 0.3029549426680, 5 }, + { 'c', 0.15, 4 }, + { 'g', 0.12, 3 }, + { 't', 0.27, 2 }, + }; + int after = 90; + printf("*%d,%d,%d,%d,%d,%d*\\n", before, iub[0].c, int(iub[1].p*100), iub[2].pi, int(iub[0].p*10000), after); + return 0; + } + ''' + self.do_run(src, '*70,97,15,3,3029,90*') + + def test_bigarray(self): + if self.emcc_args is None: return self.skip('need ta2 to compress type data on zeroinitializers') + + # avoid "array initializer too large" errors + src = r''' + #include + #include + + #define SIZE (1024*100) + struct Struct { + char x; + int y; + }; + Struct buffy[SIZE]; + + int main() { + for (int i = 0; i < SIZE; i++) { assert(buffy[i].x == 0 && buffy[i].y == 0); } // we were zeroinitialized + for (int i = 0; i < SIZE; i++) { buffy[i].x = i*i; buffy[i].y = i*i*i; } // we can save data + printf("*%d*\n", buffy[SIZE/3].x); + return 0; + } + ''' + self.do_run(src, '*57*') + + def test_mod_globalstruct(self): + src = ''' + #include + + struct malloc_params { + size_t magic, page_size; + }; + + malloc_params mparams; + + #define SIZE_T_ONE ((size_t)1) + #define page_align(S) (((S) + (mparams.page_size - SIZE_T_ONE)) & ~(mparams.page_size - SIZE_T_ONE)) + + int main() + { + mparams.page_size = 4096; + printf("*%d,%d,%d,%d*\\n", mparams.page_size, page_align(1000), page_align(6000), page_align(66474)); + return 0; + } + ''' + self.do_run(src, '*4096,4096,8192,69632*') + + def test_pystruct(self): + src = ''' + #include + + // Based on CPython code + union PyGC_Head { + struct { + union PyGC_Head *gc_next; + union PyGC_Head *gc_prev; + size_t gc_refs; + } gc; + long double dummy; /* force worst-case alignment */ + } ; + + struct gc_generation { + PyGC_Head head; + int threshold; /* collection threshold */ + int count; /* count of allocations or collections of younger + generations */ + }; + + #define NUM_GENERATIONS 3 + #define GEN_HEAD(n) (&generations[n].head) + + /* linked lists of container objects */ + static struct gc_generation generations[NUM_GENERATIONS] = { + /* PyGC_Head, threshold, count */ + {{{GEN_HEAD(0), GEN_HEAD(0), 0}}, 700, 0}, + {{{GEN_HEAD(1), GEN_HEAD(1), 0}}, 10, 0}, + {{{GEN_HEAD(2), GEN_HEAD(2), 0}}, 10, 0}, + }; + + int main() + { + gc_generation *n = NULL; + printf("*%d,%d,%d,%d,%d,%d,%d,%d*\\n", + (int)(&n[0]), + (int)(&n[0].head), + (int)(&n[0].head.gc.gc_next), + (int)(&n[0].head.gc.gc_prev), + (int)(&n[0].head.gc.gc_refs), + (int)(&n[0].threshold), (int)(&n[0].count), (int)(&n[1]) + ); + printf("*%d,%d,%d*\\n", + (int)(&generations[0]) == + (int)(&generations[0].head.gc.gc_next), + (int)(&generations[0]) == + (int)(&generations[0].head.gc.gc_prev), + (int)(&generations[0]) == + (int)(&generations[1]) + ); + int x1 = (int)(&generations[0]); + int x2 = (int)(&generations[1]); + printf("*%d*\\n", x1 == x2); + for (int i = 0; i < NUM_GENERATIONS; i++) { + PyGC_Head *list = GEN_HEAD(i); + printf("%d:%d,%d\\n", i, (int)list == (int)(list->gc.gc_prev), (int)list ==(int)(list->gc.gc_next)); + } + printf("*%d,%d,%d*\\n", sizeof(PyGC_Head), sizeof(gc_generation), int(GEN_HEAD(2)) - int(GEN_HEAD(1))); + } + ''' + if Settings.QUANTUM_SIZE == 1: + # Compressed memory. Note that sizeof() does give the fat sizes, however! + self.do_run(src, '*0,0,0,1,2,3,4,5*\n*1,0,0*\n*0*\n0:1,1\n1:1,1\n2:1,1\n*12,20,5*') + else: + if self.is_le32(): + self.do_run(src, '*0,0,0,4,8,16,20,24*\n*1,0,0*\n*0*\n0:1,1\n1:1,1\n2:1,1\n*16,24,24*') + else: + self.do_run(src, '*0,0,0,4,8,12,16,20*\n*1,0,0*\n*0*\n0:1,1\n1:1,1\n2:1,1\n*12,20,20*') + + def test_ptrtoint(self): + if self.emcc_args is None: return self.skip('requires emcc') + src = ''' + #include + + int main( int argc, const char *argv[] ) { + char *a = new char[10]; + char *a0 = a+0; + char *a5 = a+5; + int *b = new int[10]; + int *b0 = b+0; + int *b5 = b+5; + int c = (int)b5-(int)b0; // Emscripten should warn! + int d = (int)b5-(int)b0; // Emscripten should warn! + printf("*%d*\\n", (int)a5-(int)a0); + return 0; + } + ''' + runner = self + def check_warnings(output): + runner.assertEquals(filter(lambda line: 'Warning' in line, output.split('\n')).__len__(), 4) + self.do_run(src, '*5*', output_processor=check_warnings) + + def test_sizeof(self): + if self.emcc_args is None: return self.skip('requires emcc') + # Has invalid writes between printouts + Settings.SAFE_HEAP = 0 + + src = ''' + #include + #include + #include "emscripten.h" + + struct A { int x, y; }; + + int main( int argc, const char *argv[] ) { + int *a = new int[10]; + int *b = new int[1]; + int *c = new int[10]; + for (int i = 0; i < 10; i++) + a[i] = 2; + *b = 5; + for (int i = 0; i < 10; i++) + c[i] = 8; + printf("*%d,%d,%d,%d,%d*\\n", a[0], a[9], *b, c[0], c[9]); + // Should overwrite a, but not touch b! + memcpy(a, c, 10*sizeof(int)); + printf("*%d,%d,%d,%d,%d*\\n", a[0], a[9], *b, c[0], c[9]); + + // Part 2 + A as[3] = { { 5, 12 }, { 6, 990 }, { 7, 2 } }; + memcpy(&as[0], &as[2], sizeof(A)); + + printf("*%d,%d,%d,%d,%d,%d*\\n", as[0].x, as[0].y, as[1].x, as[1].y, as[2].x, as[2].y); + return 0; + } + ''' + self.do_run(src, '*2,2,5,8,8***8,8,5,8,8***7,2,6,990,7,2*', [], lambda x, err: x.replace('\n', '*')) + + def test_float_h(self): + process = Popen([PYTHON, EMCC, path_from_root('tests', 'float+.c')], stdout=PIPE, stderr=PIPE) + process.communicate() + assert process.returncode is 0, 'float.h should agree with our system' + + def test_emscripten_api(self): + #if Settings.MICRO_OPTS or Settings.RELOOP or Building.LLVM_OPTS: return self.skip('FIXME') + + src = r''' + #include + #include "emscripten.h" + + extern "C" { + void save_me_aimee() { printf("mann\n"); } + } + + int main() { + // EMSCRIPTEN_COMMENT("hello from the source"); + emscripten_run_script("Module.print('hello world' + '!')"); + printf("*%d*\n", emscripten_run_script_int("5*20")); + printf("*%s*\n", emscripten_run_script_string("'five'+'six'")); + emscripten_run_script("Module['_save_me_aimee']()"); + return 0; + } + ''' + + check = ''' +def process(filename): + src = open(filename, 'r').read() + # TODO: restore this (see comment in emscripten.h) assert '// hello from the source' in src +''' + Settings.EXPORTED_FUNCTIONS = ['_main', '_save_me_aimee'] + self.do_run(src, 'hello world!\n*100*\n*fivesix*\nmann\n', post_build=check) + + # test EXPORT_ALL + Settings.EXPORTED_FUNCTIONS = [] + Settings.EXPORT_ALL = 1 + self.do_run(src, 'hello world!\n*100*\n*fivesix*\nmann\n', post_build=check) + + def test_inlinejs(self): + if Settings.ASM_JS: return self.skip('asm does not support random code, TODO: something that works in asm') + src = r''' + #include + + double get() { + double ret = 0; + __asm __volatile__("Math.abs(-12/3.3)":"=r"(ret)); // write to a variable + return ret; + } + + int main() { + asm("Module.print('Inline JS is very cool')"); + printf("%.2f\n", get()); + return 0; + } + ''' + + self.do_run(src, 'Inline JS is very cool\n3.64') + + def test_inlinejs2(self): + if Settings.ASM_JS: return self.skip('asm does not support random code, TODO: something that works in asm') + src = r''' + #include + + int mix(int x, int y) { + int ret; + asm("Math.pow(2, %0+%1+1)" : "=r"(ret) : "r"(x), "r"(y)); // read and write + return ret; + } + + void mult() { + asm("var $_$1 = Math.abs(-100); $_$1 *= 2;"); // multiline + asm __volatile__("Module.print($_$1); Module.print('\n')"); + } + + int main(int argc, char **argv) { + printf("%d\n", mix(argc, argc/2)); + mult(); + return 0; + } + ''' + + self.do_run(src, '4\n200\n') + + def test_memorygrowth(self): + if Settings.USE_TYPED_ARRAYS == 0: return self.skip('memory growth is only supported with typed arrays') + if Settings.ASM_JS: return self.skip('asm does not support memory growth yet') + + # With typed arrays in particular, it is dangerous to use more memory than TOTAL_MEMORY, + # since we then need to enlarge the heap(s). + src = r''' + #include + #include + #include + #include + #include "emscripten.h" + + int main(int argc, char **argv) + { + char *buf1 = (char*)malloc(100); + char *data1 = "hello"; + memcpy(buf1, data1, strlen(data1)+1); + + float *buf2 = (float*)malloc(100); + float pie = 4.955; + memcpy(buf2, &pie, sizeof(float)); + + printf("*pre: %s,%.3f*\n", buf1, buf2[0]); + + int totalMemory = emscripten_run_script_int("TOTAL_MEMORY"); + char *buf3 = (char*)malloc(totalMemory+1); + buf3[argc] = (int)buf2; + if (argc % 7 == 6) printf("%d\n", memcpy(buf3, buf1, argc)); + char *buf4 = (char*)malloc(100); + float *buf5 = (float*)malloc(100); + //printf("totalMemory: %d bufs: %d,%d,%d,%d,%d\n", totalMemory, buf1, buf2, buf3, buf4, buf5); + assert((int)buf4 > (int)totalMemory && (int)buf5 > (int)totalMemory); + + printf("*%s,%.3f*\n", buf1, buf2[0]); // the old heap data should still be there + + memcpy(buf4, buf1, strlen(data1)+1); + memcpy(buf5, buf2, sizeof(float)); + printf("*%s,%.3f*\n", buf4, buf5[0]); // and the new heap space should work too + + return 0; + } + ''' + + # Fail without memory growth + self.do_run(src, 'Cannot enlarge memory arrays.') + fail = open('src.cpp.o.js').read() + + # Win with it + Settings.ALLOW_MEMORY_GROWTH = 1 + self.do_run(src, '*pre: hello,4.955*\n*hello,4.955*\n*hello,4.955*') + win = open('src.cpp.o.js').read() + + if self.emcc_args and '-O2' in self.emcc_args: + # Make sure ALLOW_MEMORY_GROWTH generates different code (should be less optimized) + code_start = 'var TOTAL_MEMORY = ' + fail = fail[fail.find(code_start):] + win = win[win.find(code_start):] + assert len(fail) < len(win), 'failing code - without memory growth on - is more optimized, and smaller' + + def test_ssr(self): # struct self-ref + src = ''' + #include + + // see related things in openjpeg + typedef struct opj_mqc_state { + unsigned int qeval; + int mps; + struct opj_mqc_state *nmps; + struct opj_mqc_state *nlps; + } opj_mqc_state_t; + + static opj_mqc_state_t mqc_states[2] = { + {0x5600, 0, &mqc_states[2], &mqc_states[3]}, + {0x5602, 1, &mqc_states[3], &mqc_states[2]}, + }; + + int main() { + printf("*%d*\\n", (int)(mqc_states+1)-(int)mqc_states); + for (int i = 0; i < 2; i++) + printf("%d:%d,%d,%d,%d\\n", i, mqc_states[i].qeval, mqc_states[i].mps, + (int)mqc_states[i].nmps-(int)mqc_states, (int)mqc_states[i].nlps-(int)mqc_states); + return 0; + } + ''' + if Settings.QUANTUM_SIZE == 1: + self.do_run(src, '''*4*\n0:22016,0,8,12\n1:22018,1,12,8\n''') + else: + self.do_run(src, '''*16*\n0:22016,0,32,48\n1:22018,1,48,32\n''') + + def test_tinyfuncstr(self): + if self.emcc_args is None: return self.skip('requires emcc') + src = ''' + #include + + struct Class { + static char *name1() { return "nameA"; } + char *name2() { return "nameB"; } + }; + + int main() { + printf("*%s,%s*\\n", Class::name1(), (new Class())->name2()); + return 0; + } + ''' + self.do_run(src, '*nameA,nameB*') + + def test_llvmswitch(self): + Settings.CORRECT_SIGNS = 1 + + src = ''' + #include + #include + + int switcher(int p) + { + switch(p) { + case 'a': + case 'b': + case 'c': + return p-1; + case -15: + return p+1; + } + return p; + } + + int main( int argc, const char *argv[] ) { + unsigned int x = 0xfffffff1; + x >>= (argc-1); // force it to be unsigned for purpose of checking our switch comparison in signed/unsigned + printf("*%d,%d,%d,%d,%d,%d*\\n", switcher('a'), switcher('b'), switcher('c'), switcher(x), switcher(-15), switcher('e')); + return 0; + } + ''' + self.do_run(src, '*96,97,98,-14,-14,101*') + + # By default, when user has not specified a -std flag, Emscripten should always build .cpp files using the C++03 standard, + # i.e. as if "-std=c++03" had been passed on the command line. On Linux with Clang 3.2 this is the case, but on Windows + # with Clang 3.2 -std=c++11 has been chosen as default, because of + # < jrose> clb: it's deliberate, with the idea that for people who don't care about the standard, they should be using the "best" thing we can offer on that platform + def test_cxx03_do_run(self): + src = ''' + #include + + #if __cplusplus != 199711L + #error By default, if no -std is specified, emscripten should be compiling with -std=c++03! + #endif + + int main( int argc, const char *argv[] ) { + printf("Hello world!\\n"); + return 0; + } + ''' + self.do_run(src, 'Hello world!') + + def test_bigswitch(self): + if Settings.RELOOP: return self.skip('TODO: switch in relooper, issue #781') + if Settings.ASM_JS: return self.skip('TODO: switch too large for asm') + + src = open(path_from_root('tests', 'bigswitch.cpp')).read() + self.do_run(src, '''34962: GL_ARRAY_BUFFER (0x8892) +26214: what? +35040: GL_STREAM_DRAW (0x88E0) +''', args=['34962', '26214', '35040']) + + def test_indirectbr(self): + Building.COMPILER_TEST_OPTS = filter(lambda x: x != '-g', Building.COMPILER_TEST_OPTS) + + src = ''' + #include + int main(void) { + const void *addrs[2] = { &&FOO, &&BAR }; + + // confuse the optimizer so it doesn't hardcode the jump and avoid generating an |indirectbr| instruction + int which = 0; + for (int x = 0; x < 1000; x++) which = (which + x*x) % 7; + which = (which % 2) + 1; + + goto *addrs[which]; + + FOO: + printf("bad\\n"); + return 0; + BAR: + printf("good\\n"); + const void *addr = &&FOO; + goto *addr; + } + ''' + self.do_run(src, 'good\nbad') + + def test_indirectbr_many(self): + if Settings.USE_TYPED_ARRAYS != 2: return self.skip('blockaddr > 255 requires ta2') + + blocks = range(1500) + init = ', '.join(['&&B%d' % b for b in blocks]) + defs = '\n'.join(['B%d: printf("%d\\n"); return 0;' % (b,b) for b in blocks]) + src = ''' + #include + int main(int argc, char **argv) { + printf("\\n"); + const void *addrs[] = { %s }; + goto *addrs[argc*argc + 1000]; + +%s + return 0; + } + ''' % (init, defs) + self.do_run(src, '\n1001\n') + + def test_pack(self): + src = ''' + #include + #include + + #pragma pack(push,1) + typedef struct header + { + unsigned char id; + unsigned short colour; + unsigned char desc; + } header; + #pragma pack(pop) + + typedef struct fatheader + { + unsigned char id; + unsigned short colour; + unsigned char desc; + } fatheader; + + int main( int argc, const char *argv[] ) { + header h, *ph = 0; + fatheader fh, *pfh = 0; + printf("*%d,%d,%d*\\n", sizeof(header), (int)((int)&h.desc - (int)&h.id), (int)(&ph[1])-(int)(&ph[0])); + printf("*%d,%d,%d*\\n", sizeof(fatheader), (int)((int)&fh.desc - (int)&fh.id), (int)(&pfh[1])-(int)(&pfh[0])); + return 0; + } + ''' + if Settings.QUANTUM_SIZE == 1: + self.do_run(src, '*4,2,3*\n*6,2,3*') + else: + self.do_run(src, '*4,3,4*\n*6,4,6*') + + def test_varargs(self): + if Settings.QUANTUM_SIZE == 1: return self.skip('FIXME: Add support for this') + if not self.is_le32(): return self.skip('we do not support all varargs stuff without le32') + + src = ''' + #include + #include + + void vary(const char *s, ...) + { + va_list v; + va_start(v, s); + char d[20]; + vsnprintf(d, 20, s, v); + puts(d); + + // Try it with copying + va_list tempva; + va_copy(tempva, v); + vsnprintf(d, 20, s, tempva); + puts(d); + + va_end(v); + } + + void vary2(char color, const char *s, ...) + { + va_list v; + va_start(v, s); + char d[21]; + d[0] = color; + vsnprintf(d+1, 20, s, v); + puts(d); + va_end(v); + } + + void varargs_listoffsets_list_evaluate(int count, va_list ap, int vaIteration) + { + while(count > 0) + { + const char* string = va_arg(ap, const char*); + printf("%s", string); + count--; + } + printf("\\n"); + } + + void varags_listoffsets_list_copy(int count, va_list ap, int iteration) + { + va_list ap_copy; + va_copy(ap_copy, ap); + varargs_listoffsets_list_evaluate(count, ap_copy, iteration); + va_end(ap_copy); + } + + void varargs_listoffsets_args(int type, int count, ...) + { + va_list ap; + va_start(ap, count); + + // evaluate a copied list + varags_listoffsets_list_copy(count, ap, 1); + varags_listoffsets_list_copy(count, ap, 2); + varags_listoffsets_list_copy(count, ap, 3); + varags_listoffsets_list_copy(count, ap, 4); + + varargs_listoffsets_list_evaluate(count, ap, 1); + + // NOTE: we expect this test to fail, so we will check the stdout for ..... + varargs_listoffsets_list_evaluate(count, ap, 2); + + // NOTE: this test has to work again, as we restart the list + va_end(ap); + va_start(ap, count); + varargs_listoffsets_list_evaluate(count, ap, 3); + va_end(ap); + } + + void varargs_listoffsets_main() + { + varargs_listoffsets_args(0, 5, "abc", "def", "ghi", "jkl", "mno", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""); + } + + #define GETMAX(pref, type) \ + type getMax##pref(int num, ...) \ + { \ + va_list vv; \ + va_start(vv, num); \ + type maxx = va_arg(vv, type); \ + for (int i = 1; i < num; i++) \ + { \ + type curr = va_arg(vv, type); \ + maxx = curr > maxx ? curr : maxx; \ + } \ + va_end(vv); \ + return maxx; \ + } + GETMAX(i, int); + GETMAX(D, double); + + int main(int argc, char **argv) { + vary("*cheez: %d+%d*", 0, 24); // Also tests that '0' is not special as an array ender + vary("*albeit*"); // Should not fail with no var args in vararg function + vary2('Q', "%d*", 85); + + int maxxi = getMaxi(6, 2, 5, 21, 4, -10, 19); + printf("maxxi:%d*\\n", maxxi); + double maxxD = getMaxD(6, (double)2.1, (double)5.1, (double)22.1, (double)4.1, (double)-10.1, (double)19.1, (double)2); + printf("maxxD:%.2f*\\n", (float)maxxD); + + // And, as a function pointer + void (*vfp)(const char *s, ...) = argc == 1211 ? NULL : vary; + vfp("*vfp:%d,%d*", 22, 199); + + // ensure lists work properly when copied, reinited etc. + varargs_listoffsets_main(); + + return 0; + } + ''' + self.do_run(src, '*cheez: 0+24*\n*cheez: 0+24*\n*albeit*\n*albeit*\nQ85*\nmaxxi:21*\nmaxxD:22.10*\n*vfp:22,199*\n*vfp:22,199*\n'+ + 'abcdefghijklmno\nabcdefghijklmno\nabcdefghijklmno\nabcdefghijklmno\nabcdefghijklmno\n\nabcdefghijklmno\n') + + def test_varargs_byval(self): + if Settings.USE_TYPED_ARRAYS != 2: return self.skip('FIXME: Add support for this') + if self.is_le32(): return self.skip('clang cannot compile this code with that target yet') + + src = r''' + #include + #include + + typedef struct type_a { + union { + double f; + void *p; + int i; + short sym; + } value; + } type_a; + + enum mrb_vtype { + MRB_TT_FALSE = 0, /* 0 */ + MRB_TT_CLASS = 9 /* 9 */ + }; + + typedef struct type_b { + enum mrb_vtype tt:8; + } type_b; + + void print_type_a(int argc, ...); + void print_type_b(int argc, ...); + + int main(int argc, char *argv[]) + { + type_a a; + type_b b; + a.value.p = (void*) 0x12345678; + b.tt = MRB_TT_CLASS; + + printf("The original address of a is: %p\n", a.value.p); + printf("The original type of b is: %d\n", b.tt); + + print_type_a(1, a); + print_type_b(1, b); + + return 0; + } + + void print_type_a(int argc, ...) { + va_list ap; + type_a a; + + va_start(ap, argc); + a = va_arg(ap, type_a); + va_end(ap); + + printf("The current address of a is: %p\n", a.value.p); + } + + void print_type_b(int argc, ...) { + va_list ap; + type_b b; + + va_start(ap, argc); + b = va_arg(ap, type_b); + va_end(ap); + + printf("The current type of b is: %d\n", b.tt); + } + ''' + self.do_run(src, '''The original address of a is: 0x12345678 +The original type of b is: 9 +The current address of a is: 0x12345678 +The current type of b is: 9 +''') + + def test_functionpointer_libfunc_varargs(self): + src = r''' + #include + #include + typedef int (*fp_t)(int, int, ...); + int main(int argc, char **argv) { + fp_t fp = &fcntl; + if (argc == 1337) fp = (fp_t)&main; + (*fp)(0, 10); + (*fp)(0, 10, 5); + printf("waka\n"); + return 0; + } + ''' + self.do_run(src, '''waka''') + + def test_structbyval(self): + Settings.INLINING_LIMIT = 50 + + # part 1: make sure that normally, passing structs by value works + + src = r''' + #include + + struct point + { + int x, y; + }; + + void dump(struct point p) { + p.x++; // should not modify + p.y++; // anything in the caller! + printf("dump: %d,%d\n", p.x, p.y); + } + + void dumpmod(struct point *p) { + p->x++; // should not modify + p->y++; // anything in the caller! + printf("dump: %d,%d\n", p->x, p->y); + } + + int main( int argc, const char *argv[] ) { + point p = { 54, 2 }; + printf("pre: %d,%d\n", p.x, p.y); + dump(p); + void (*dp)(point p) = dump; // And, as a function pointer + dp(p); + printf("post: %d,%d\n", p.x, p.y); + dumpmod(&p); + dumpmod(&p); + printf("last: %d,%d\n", p.x, p.y); + return 0; + } + ''' + self.do_run(src, 'pre: 54,2\ndump: 55,3\ndump: 55,3\npost: 54,2\ndump: 55,3\ndump: 56,4\nlast: 56,4') + + # Check for lack of warning in the generated code (they should appear in part 2) + generated = open(os.path.join(self.get_dir(), 'src.cpp.o.js')).read() + assert 'Casting a function pointer type to another with a different number of arguments.' not in generated, 'Unexpected warning' + + # part 2: make sure we warn about mixing c and c++ calling conventions here + + if not (self.emcc_args is None or self.emcc_args == []): return # Optimized code is missing the warning comments + + header = r''' + struct point + { + int x, y; + }; + + ''' + open(os.path.join(self.get_dir(), 'header.h'), 'w').write(header) + + supp = r''' + #include + #include "header.h" + + void dump(struct point p) { + p.x++; // should not modify + p.y++; // anything in the caller! + printf("dump: %d,%d\n", p.x, p.y); + } + ''' + supp_name = os.path.join(self.get_dir(), 'supp.c') + open(supp_name, 'w').write(supp) + + main = r''' + #include + #include "header.h" + + #ifdef __cplusplus + extern "C" { + #endif + void dump(struct point p); + #ifdef __cplusplus + } + #endif + + int main( int argc, const char *argv[] ) { + struct point p = { 54, 2 }; + printf("pre: %d,%d\n", p.x, p.y); + dump(p); + void (*dp)(struct point p) = dump; // And, as a function pointer + dp(p); + printf("post: %d,%d\n", p.x, p.y); + return 0; + } + ''' + main_name = os.path.join(self.get_dir(), 'main.cpp') + open(main_name, 'w').write(main) + + Building.emcc(supp_name) + Building.emcc(main_name) + all_name = os.path.join(self.get_dir(), 'all.bc') + Building.link([supp_name + '.o', main_name + '.o'], all_name) + + # This will fail! See explanation near the warning we check for, in the compiler source code + output = Popen([PYTHON, EMCC, all_name], stderr=PIPE).communicate() + + # Check for warning in the generated code + generated = open(os.path.join(self.get_dir(), 'src.cpp.o.js')).read() + if 'i386-pc-linux-gnu' in COMPILER_OPTS: + assert 'Casting a function pointer type to a potentially incompatible one' in output[1], 'Missing expected warning' + else: + print >> sys.stderr, 'skipping C/C++ conventions warning check, since not i386-pc-linux-gnu' + + def test_stdlibs(self): + if self.emcc_args is None: return self.skip('requires emcc') + if Settings.USE_TYPED_ARRAYS == 2: + # Typed arrays = 2 + safe heap prints a warning that messes up our output. + Settings.SAFE_HEAP = 0 + src = ''' + #include + #include + #include + + void clean() + { + printf("*cleaned*\\n"); + } + + int comparer(const void *a, const void *b) { + int aa = *((int*)a); + int bb = *((int*)b); + return aa - bb; + } + + int main() { + // timeofday + timeval t; + gettimeofday(&t, NULL); + printf("*%d,%d\\n", int(t.tv_sec), int(t.tv_usec)); // should not crash + + // atexit + atexit(clean); + + // qsort + int values[6] = { 3, 2, 5, 1, 5, 6 }; + qsort(values, 5, sizeof(int), comparer); + printf("*%d,%d,%d,%d,%d,%d*\\n", values[0], values[1], values[2], values[3], values[4], values[5]); + + printf("*stdin==0:%d*\\n", stdin == 0); // check that external values are at least not NULL + printf("*%%*\\n"); + printf("*%.1ld*\\n", 5); + + printf("*%.1f*\\n", strtod("66", NULL)); // checks dependency system, as our strtod needs _isspace etc. + + printf("*%ld*\\n", strtol("10", NULL, 0)); + printf("*%ld*\\n", strtol("0", NULL, 0)); + printf("*%ld*\\n", strtol("-10", NULL, 0)); + printf("*%ld*\\n", strtol("12", NULL, 16)); + + printf("*%lu*\\n", strtoul("10", NULL, 0)); + printf("*%lu*\\n", strtoul("0", NULL, 0)); + printf("*%lu*\\n", strtoul("-10", NULL, 0)); + + printf("*malloc(0)!=0:%d*\\n", malloc(0) != 0); // We should not fail horribly + + return 0; + } + ''' + + self.do_run(src, '*1,2,3,5,5,6*\n*stdin==0:0*\n*%*\n*5*\n*66.0*\n*10*\n*0*\n*-10*\n*18*\n*10*\n*0*\n*4294967286*\n*malloc(0)!=0:1*\n*cleaned*') + + src = r''' + #include + #include + + int main() { + bool x = true; + bool y = false; + printf("*%d*\n", x != y); + return 0; + } + ''' + + self.do_run(src, '*1*', force_c=True) + + def test_strtoll_hex(self): + if self.emcc_args is None: return self.skip('requires emcc') + + # tests strtoll for hex strings (0x...) + src = r''' + #include + #include + + int main() { + const char *STRING = "0x4 -0x3A +0xDEADBEEF"; + char *end_char; + + // undefined base + long long int l1 = strtoll(STRING, &end_char, 0); + long long int l2 = strtoll(end_char, &end_char, 0); + long long int l3 = strtoll(end_char, NULL, 0); + + // defined base + long long int l4 = strtoll(STRING, &end_char, 16); + long long int l5 = strtoll(end_char, &end_char, 16); + long long int l6 = strtoll(end_char, NULL, 16); + + printf("%d%d%d%d%d%d\n", l1==0x4, l2==-0x3a, l3==0xdeadbeef, l4==0x4, l5==-0x3a, l6==0xdeadbeef); + return 0; + } + ''' + self.do_run(src, '111111') + + def test_strtoll_dec(self): + if self.emcc_args is None: return self.skip('requires emcc') + + # tests strtoll for decimal strings (0x...) + src = r''' + #include + #include + + int main() { + const char *STRING = "4 -38 +4711"; + char *end_char; + + // undefined base + long long int l1 = strtoll(STRING, &end_char, 0); + long long int l2 = strtoll(end_char, &end_char, 0); + long long int l3 = strtoll(end_char, NULL, 0); + + // defined base + long long int l4 = strtoll(STRING, &end_char, 10); + long long int l5 = strtoll(end_char, &end_char, 10); + long long int l6 = strtoll(end_char, NULL, 10); + + printf("%d%d%d%d%d%d\n", l1==4, l2==-38, l3==4711, l4==4, l5==-38, l6==4711); + return 0; + } + ''' + self.do_run(src, '111111') + + def test_strtoll_bin(self): + if self.emcc_args is None: return self.skip('requires emcc') + + # tests strtoll for binary strings (0x...) + src = r''' + #include + #include + + int main() { + const char *STRING = "1 -101 +1011"; + char *end_char; + + // defined base + long long int l4 = strtoll(STRING, &end_char, 2); + long long int l5 = strtoll(end_char, &end_char, 2); + long long int l6 = strtoll(end_char, NULL, 2); + + printf("%d%d%d\n", l4==1, l5==-5, l6==11); + return 0; + } + ''' + self.do_run(src, '111') + + def test_strtoll_oct(self): + if self.emcc_args is None: return self.skip('requires emcc') + + # tests strtoll for decimal strings (0x...) + src = r''' + #include + #include + + int main() { + const char *STRING = "0 -035 +04711"; + char *end_char; + + // undefined base + long long int l1 = strtoll(STRING, &end_char, 0); + long long int l2 = strtoll(end_char, &end_char, 0); + long long int l3 = strtoll(end_char, NULL, 0); + + // defined base + long long int l4 = strtoll(STRING, &end_char, 8); + long long int l5 = strtoll(end_char, &end_char, 8); + long long int l6 = strtoll(end_char, NULL, 8); + + printf("%d%d%d%d%d%d\n", l1==0, l2==-29, l3==2505, l4==0, l5==-29, l6==2505); + return 0; + } + ''' + self.do_run(src, '111111') + + def test_strtol_hex(self): + # tests strtoll for hex strings (0x...) + src = r''' + #include + #include + + int main() { + const char *STRING = "0x4 -0x3A +0xDEAD"; + char *end_char; + + // undefined base + long l1 = strtol(STRING, &end_char, 0); + long l2 = strtol(end_char, &end_char, 0); + long l3 = strtol(end_char, NULL, 0); + + // defined base + long l4 = strtol(STRING, &end_char, 16); + long l5 = strtol(end_char, &end_char, 16); + long l6 = strtol(end_char, NULL, 16); + + printf("%d%d%d%d%d%d\n", l1==0x4, l2==-0x3a, l3==0xdead, l4==0x4, l5==-0x3a, l6==0xdead); + return 0; + } + ''' + self.do_run(src, '111111') + + def test_strtol_dec(self): + # tests strtoll for decimal strings (0x...) + src = r''' + #include + #include + + int main() { + const char *STRING = "4 -38 +4711"; + char *end_char; + + // undefined base + long l1 = strtol(STRING, &end_char, 0); + long l2 = strtol(end_char, &end_char, 0); + long l3 = strtol(end_char, NULL, 0); + + // defined base + long l4 = strtol(STRING, &end_char, 10); + long l5 = strtol(end_char, &end_char, 10); + long l6 = strtol(end_char, NULL, 10); + + printf("%d%d%d%d%d%d\n", l1==4, l2==-38, l3==4711, l4==4, l5==-38, l6==4711); + return 0; + } + ''' + self.do_run(src, '111111') + + def test_strtol_bin(self): + # tests strtoll for binary strings (0x...) + src = r''' + #include + #include + + int main() { + const char *STRING = "1 -101 +1011"; + char *end_char; + + // defined base + long l4 = strtol(STRING, &end_char, 2); + long l5 = strtol(end_char, &end_char, 2); + long l6 = strtol(end_char, NULL, 2); + + printf("%d%d%d\n", l4==1, l5==-5, l6==11); + return 0; + } + ''' + self.do_run(src, '111') + + def test_strtol_oct(self): + # tests strtoll for decimal strings (0x...) + src = r''' + #include + #include + + int main() { + const char *STRING = "0 -035 +04711"; + char *end_char; + + // undefined base + long l1 = strtol(STRING, &end_char, 0); + long l2 = strtol(end_char, &end_char, 0); + long l3 = strtol(end_char, NULL, 0); + + // defined base + long l4 = strtol(STRING, &end_char, 8); + long l5 = strtol(end_char, &end_char, 8); + long l6 = strtol(end_char, NULL, 8); + + printf("%d%d%d%d%d%d\n", l1==0, l2==-29, l3==2505, l4==0, l5==-29, l6==2505); + return 0; + } + ''' + self.do_run(src, '111111') + + def test_atexit(self): + # Confirms they are called in reverse order + src = r''' + #include + #include + + static void cleanA() { + printf("A"); + } + static void cleanB() { + printf("B"); + } + + int main() { + atexit(cleanA); + atexit(cleanB); + return 0; + } + ''' + self.do_run(src, 'BA') + + def test_time(self): + # XXX Not sure what the right output is here. Looks like the test started failing with daylight savings changes. Modified it to pass again. + src = open(path_from_root('tests', 'time', 'src.c'), 'r').read() + expected = open(path_from_root('tests', 'time', 'output.txt'), 'r').read() + expected2 = open(path_from_root('tests', 'time', 'output2.txt'), 'r').read() + self.do_run(src, [expected, expected2], + extra_emscripten_args=['-H', 'libc/time.h']) + #extra_emscripten_args=['-H', 'libc/fcntl.h,libc/sys/unistd.h,poll.h,libc/math.h,libc/langinfo.h,libc/time.h']) + + def test_timeb(self): + # Confirms they are called in reverse order + src = r''' + #include + #include + #include + + int main() { + timeb tb; + tb.timezone = 1; + printf("*%d\n", ftime(&tb)); + assert(tb.time > 10000); + assert(tb.timezone == 0); + assert(tb.dstflag == 0); + return 0; + } + ''' + self.do_run(src, '*0\n') + + def test_time_c(self): + src = r''' + #include + #include + + int main() { + time_t t = time(0); + printf("time: %s\n", ctime(&t)); + } + ''' + self.do_run(src, 'time: ') # compilation check, mainly + + def test_gmtime(self): + src = r''' + #include + #include + #include + #include + + int main(void) + { + time_t t=time(NULL); + struct tm *ptm=gmtime(&t); + struct tm tmCurrent=*ptm; + int hour=tmCurrent.tm_hour; + + t-=hour*3600; // back to midnight + int yday = -1; + for(hour=0;hour<24;hour++) + { + ptm=gmtime(&t); + // tm_yday must be constant all day... + printf("yday: %d, hour: %d\n", ptm->tm_yday, hour); + if (yday == -1) yday = ptm->tm_yday; + else assert(yday == ptm->tm_yday); + t+=3600; // add one hour + } + printf("ok!\n"); + return(0); + } + ''' + self.do_run(src, '''ok!''') + + def test_strptime_tm(self): + src=r''' + #include + #include + #include + + int main() { + struct tm tm; + char *ptr = strptime("17410105012000", "%H%M%S%d%m%Y", &tm); + + printf("%s: %s, %d/%d/%d %d:%d:%d", + (ptr != NULL && *ptr=='\0') ? "OK" : "ERR", + tm.tm_wday == 0 ? "Sun" : (tm.tm_wday == 1 ? "Mon" : (tm.tm_wday == 2 ? "Tue" : (tm.tm_wday == 3 ? "Wed" : (tm.tm_wday == 4 ? "Thu" : (tm.tm_wday == 5 ? "Fri" : (tm.tm_wday == 6 ? "Sat" : "ERR")))))), + tm.tm_mon+1, + tm.tm_mday, + tm.tm_year+1900, + tm.tm_hour, + tm.tm_min, + tm.tm_sec + ); + } + ''' + self.do_run(src, 'OK: Wed, 1/5/2000 17:41:1') + + def test_strptime_days(self): + src = r''' + #include + #include + #include + + static const struct { + const char *input; + const char *format; + } day_tests[] = { + { "2000-01-01", "%Y-%m-%d"}, + { "03/03/00", "%D"}, + { "9/9/99", "%x"}, + { "19990502123412", "%Y%m%d%H%M%S"}, + { "2001 20 Mon", "%Y %U %a"}, + { "2006 4 Fri", "%Y %U %a"}, + { "2001 21 Mon", "%Y %W %a"}, + { "2013 29 Wed", "%Y %W %a"}, + { "2000-01-01 08:12:21 AM", "%Y-%m-%d %I:%M:%S %p"}, + { "2000-01-01 08:12:21 PM", "%Y-%m-%d %I:%M:%S %p"}, + { "2001 17 Tue", "%Y %U %a"}, + { "2001 8 Thursday", "%Y %W %a"}, + }; + + int main() { + struct tm tm; + + for (int i = 0; i < sizeof (day_tests) / sizeof (day_tests[0]); ++i) { + memset (&tm, '\0', sizeof (tm)); + char *ptr = strptime(day_tests[i].input, day_tests[i].format, &tm); + + printf("%s: %d/%d/%d (%dth DoW, %dth DoY)\n", (ptr != NULL && *ptr=='\0') ? "OK" : "ERR", tm.tm_mon+1, tm.tm_mday, 1900+tm.tm_year, tm.tm_wday, tm.tm_yday); + } + } + ''' + self.do_run(src, 'OK: 1/1/2000 (6th DoW, 0th DoY)\n'\ + 'OK: 3/3/2000 (5th DoW, 62th DoY)\n'\ + 'OK: 9/9/1999 (4th DoW, 251th DoY)\n'\ + 'OK: 5/2/1999 (0th DoW, 121th DoY)\n'\ + 'OK: 5/21/2001 (1th DoW, 140th DoY)\n'\ + 'OK: 1/27/2006 (5th DoW, 26th DoY)\n'\ + 'OK: 5/21/2001 (1th DoW, 140th DoY)\n'\ + 'OK: 7/24/2013 (3th DoW, 204th DoY)\n'\ + 'OK: 1/1/2000 (6th DoW, 0th DoY)\n'\ + 'OK: 1/1/2000 (6th DoW, 0th DoY)\n'\ + 'OK: 5/1/2001 (2th DoW, 120th DoY)\n'\ + 'OK: 2/22/2001 (4th DoW, 52th DoY)\n'\ + ) + + def test_strptime_reentrant(self): + src=r''' + #include + #include + #include + #include + + int main () { + int result = 0; + struct tm tm; + + memset (&tm, 0xaa, sizeof (tm)); + + /* Test we don't crash on uninitialized struct tm. + Some fields might contain bogus values until everything + needed is initialized, but we shouldn't crash. */ + if (strptime ("2007", "%Y", &tm) == NULL + || strptime ("12", "%d", &tm) == NULL + || strptime ("Feb", "%b", &tm) == NULL + || strptime ("13", "%M", &tm) == NULL + || strptime ("21", "%S", &tm) == NULL + || strptime ("16", "%H", &tm) == NULL) { + printf("ERR: returned NULL"); + exit(EXIT_FAILURE); + } + + if (tm.tm_sec != 21 || tm.tm_min != 13 || tm.tm_hour != 16 + || tm.tm_mday != 12 || tm.tm_mon != 1 || tm.tm_year != 107 + || tm.tm_wday != 1 || tm.tm_yday != 42) { + printf("ERR: unexpected tm content (1) - %d/%d/%d %d:%d:%d", tm.tm_mon+1, tm.tm_mday, tm.tm_year+1900, tm.tm_hour, tm.tm_min, tm.tm_sec); + exit(EXIT_FAILURE); + } + + if (strptime ("8", "%d", &tm) == NULL) { + printf("ERR: strptime failed"); + exit(EXIT_FAILURE); + } + + if (tm.tm_sec != 21 || tm.tm_min != 13 || tm.tm_hour != 16 + || tm.tm_mday != 8 || tm.tm_mon != 1 || tm.tm_year != 107 + || tm.tm_wday != 4 || tm.tm_yday != 38) { + printf("ERR: unexpected tm content (2) - %d/%d/%d %d:%d:%d", tm.tm_mon+1, tm.tm_mday, tm.tm_year+1900, tm.tm_hour, tm.tm_min, tm.tm_sec); + exit(EXIT_FAILURE); + } + + printf("OK"); + } + ''' + self.do_run(src, 'OK') + + def test_strftime(self): + src=r''' + #include + #include + #include + #include + + void test(int result, const char* comment, const char* parsed = "") { + printf("%d",result); + if (!result) { + printf("\nERROR: %s (\"%s\")\n", comment, parsed); + } + } + + int cmp(const char *s1, const char *s2) { + for ( ; *s1 == *s2 ; s1++,s2++ ) { + if ( *s1 == '\0' ) + break; + } + + return (*s1 - *s2); + } + + int main() { + struct tm tm; + char s[1000]; + size_t size; + + tm.tm_sec = 4; + tm.tm_min = 23; + tm.tm_hour = 20; + tm.tm_mday = 21; + tm.tm_mon = 1; + tm.tm_year = 74; + tm.tm_wday = 4; + tm.tm_yday = 51; + tm.tm_isdst = 0; + + size = strftime(s, 1000, "", &tm); + test((size==0) && (*s=='\0'), "strftime test #1", s); + + size = strftime(s, 1000, "%a", &tm); + test((size==3) && !cmp(s, "Thu"), "strftime test #2", s); + + size = strftime(s, 1000, "%A", &tm); + test((size==8) && !cmp(s, "Thursday"), "strftime test #3", s); + + size = strftime(s, 1000, "%b", &tm); + test((size==3) && !cmp(s, "Feb"), "strftime test #4", s); + + size = strftime(s, 1000, "%B", &tm); + test((size==8) && !cmp(s, "February"), + "strftime test #5", s); + + size = strftime(s, 1000, "%d", &tm); + test((size==2) && !cmp(s, "21"), + "strftime test #6", s); + + size = strftime(s, 1000, "%H", &tm); + test((size==2) && !cmp(s, "20"), + "strftime test #7", s); + + size = strftime(s, 1000, "%I", &tm); + test((size==2) && !cmp(s, "08"), + "strftime test #8", s); + + size = strftime(s, 1000, "%j", &tm); + test((size==3) && !cmp(s, "052"), + "strftime test #9", s); + + size = strftime(s, 1000, "%m", &tm); + test((size==2) && !cmp(s, "02"), + "strftime test #10", s); + + size = strftime(s, 1000, "%M", &tm); + test((size==2) && !cmp(s, "23"), + "strftime test #11", s); + + size = strftime(s, 1000, "%p", &tm); + test((size==2) && !cmp(s, "PM"), + "strftime test #12", s); + + size = strftime(s, 1000, "%S", &tm); + test((size==2) && !cmp(s, "04"), + "strftime test #13", s); + + size = strftime(s, 1000, "%U", &tm); + test((size==2) && !cmp(s, "07"), + "strftime test #14", s); + + size = strftime(s, 1000, "%w", &tm); + test((size==1) && !cmp(s, "4"), + "strftime test #15", s); + + size = strftime(s, 1000, "%W", &tm); + test((size==2) && !cmp(s, "07"), + "strftime test #16", s); + + size = strftime(s, 1000, "%y", &tm); + test((size==2) && !cmp(s, "74"), + "strftime test #17", s); + + size = strftime(s, 1000, "%Y", &tm); + test((size==4) && !cmp(s, "1974"), + "strftime test #18", s); + + size = strftime(s, 1000, "%%", &tm); + test((size==1) && !cmp(s, "%"), + "strftime test #19", s); + + size = strftime(s, 5, "%Y", &tm); + test((size==4) && !cmp(s, "1974"), + "strftime test #20", s); + + size = strftime(s, 4, "%Y", &tm); + test((size==0), "strftime test #21", s); + + tm.tm_mon = 0; + tm.tm_mday = 1; + size = strftime(s, 10, "%U", &tm); + test((size==2) && !cmp(s, "00"), "strftime test #22", s); + + size = strftime(s, 10, "%W", &tm); + test((size==2) && !cmp(s, "00"), "strftime test #23", s); + + // 1/1/1973 was a Sunday and is in CW 1 + tm.tm_year = 73; + size = strftime(s, 10, "%W", &tm); + test((size==2) && !cmp(s, "01"), "strftime test #24", s); + + // 1/1/1978 was a Monday and is in CW 1 + tm.tm_year = 78; + size = strftime(s, 10, "%U", &tm); + test((size==2) && !cmp(s, "01"), "strftime test #25", s); + + // 2/1/1999 + tm.tm_year = 99; + tm.tm_yday = 1; + size = strftime(s, 10, "%G (%V)", &tm); + test((size==9) && !cmp(s, "1998 (53)"), "strftime test #26", s); + + size = strftime(s, 10, "%g", &tm); + test((size==2) && !cmp(s, "98"), "strftime test #27", s); + + // 30/12/1997 + tm.tm_year = 97; + tm.tm_yday = 363; + size = strftime(s, 10, "%G (%V)", &tm); + test((size==9) && !cmp(s, "1998 (01)"), "strftime test #28", s); + + size = strftime(s, 10, "%g", &tm); + test((size==2) && !cmp(s, "98"), "strftime test #29", s); + } + ''' + self.do_run(src, '11111111111111111111111111111') + + def test_intentional_fault(self): + # Some programs intentionally segfault themselves, we should compile that into a throw + src = r''' + int main () { + *(volatile char *)0 = 0; + return 0; + } + ''' + self.do_run(src, 'fault on write to 0' if not Settings.ASM_JS else 'abort()') + + def test_trickystring(self): + src = r''' + #include + + typedef struct + { + int (*f)(void *); + void *d; + char s[16]; + } LMEXFunctionStruct; + + int f(void *user) + { + return 0; + } + + static LMEXFunctionStruct const a[] = + { + {f, (void *)(int)'a', "aa"} + }; + + int main() + { + printf("ok\n"); + return a[0].f(a[0].d); + } + ''' + self.do_run(src, 'ok\n') + + def test_statics(self): + # static initializers save i16 but load i8 for some reason (or i64 and load i8) + if Settings.SAFE_HEAP: + Settings.SAFE_HEAP = 3 + Settings.SAFE_HEAP_LINES = ['src.cpp:19', 'src.cpp:26', 'src.cpp:28'] + + src = ''' + #include + #include + + #define CONSTRLEN 32 + + char * (*func)(char *, const char *) = NULL; + + void conoutfv(const char *fmt) + { + static char buf[CONSTRLEN]; + func(buf, fmt); // call by function pointer to make sure we test strcpy here + puts(buf); + } + + struct XYZ { + float x, y, z; + XYZ(float a, float b, float c) : x(a), y(b), z(c) { } + static const XYZ& getIdentity() + { + static XYZ iT(1,2,3); + return iT; + } + }; + struct S { + static const XYZ& getIdentity() + { + static const XYZ iT(XYZ::getIdentity()); + return iT; + } + }; + + int main() { + func = &strcpy; + conoutfv("*staticccz*"); + printf("*%.2f,%.2f,%.2f*\\n", S::getIdentity().x, S::getIdentity().y, S::getIdentity().z); + return 0; + } + ''' + self.do_run(src, '*staticccz*\n*1.00,2.00,3.00*') + + def test_copyop(self): + if self.emcc_args is None: return self.skip('requires emcc') + + # clang generated code is vulnerable to this, as it uses + # memcpy for assignments, with hardcoded numbers of bytes + # (llvm-gcc copies items one by one). See QUANTUM_SIZE in + # settings.js. + src = ''' + #include + #include + #include + + struct vec { + double x,y,z; + vec() : x(0), y(0), z(0) { }; + vec(const double a, const double b, const double c) : x(a), y(b), z(c) { }; + }; + + struct basis { + vec a, b, c; + basis(const vec& v) { + a=v; // should not touch b! + printf("*%.2f,%.2f,%.2f*\\n", b.x, b.y, b.z); + } + }; + + int main() { + basis B(vec(1,0,0)); + + // Part 2: similar problem with memset and memmove + int x = 1, y = 77, z = 2; + memset((void*)&x, 0, sizeof(int)); + memset((void*)&z, 0, sizeof(int)); + printf("*%d,%d,%d*\\n", x, y, z); + memcpy((void*)&x, (void*)&z, sizeof(int)); + memcpy((void*)&z, (void*)&x, sizeof(int)); + printf("*%d,%d,%d*\\n", x, y, z); + memmove((void*)&x, (void*)&z, sizeof(int)); + memmove((void*)&z, (void*)&x, sizeof(int)); + printf("*%d,%d,%d*\\n", x, y, z); + return 0; + } + ''' + self.do_run(src, '*0.00,0.00,0.00*\n*0,77,0*\n*0,77,0*\n*0,77,0*') + + def test_memcpy_memcmp(self): + src = ''' + #include + #include + #include + + #define MAXX 48 + void reset(unsigned char *buffer) { + for (int i = 0; i < MAXX; i++) buffer[i] = i+1; + } + void dump(unsigned char *buffer) { + for (int i = 0; i < MAXX-1; i++) printf("%2d,", buffer[i]); + printf("%d\\n", buffer[MAXX-1]); + } + int main() { + unsigned char buffer[MAXX]; + for (int i = MAXX/4; i < MAXX-MAXX/4; i++) { + for (int j = MAXX/4; j < MAXX-MAXX/4; j++) { + for (int k = 1; k < MAXX/4; k++) { + if (i == j) continue; + if (i < j && i+k > j) continue; + if (j < i && j+k > i) continue; + printf("[%d,%d,%d] ", i, j, k); + reset(buffer); + memcpy(buffer+i, buffer+j, k); + dump(buffer); + assert(memcmp(buffer+i, buffer+j, k) == 0); + buffer[i + k/2]++; + if (buffer[i + k/2] != 0) { + assert(memcmp(buffer+i, buffer+j, k) > 0); + } else { + assert(memcmp(buffer+i, buffer+j, k) < 0); + } + buffer[i + k/2]--; + buffer[j + k/2]++; + if (buffer[j + k/2] != 0) { + assert(memcmp(buffer+i, buffer+j, k) < 0); + } else { + assert(memcmp(buffer+i, buffer+j, k) > 0); + } + } + } + } + return 0; + } + ''' + def check(result, err): + return hashlib.sha1(result).hexdigest() + self.do_run(src, '6c9cdfe937383b79e52ca7a2cce83a21d9f5422c', + output_nicerizer = check) + + def test_memcpy2(self): + src = r''' + #include + #include + #include + int main() { + char buffer[256]; + for (int i = 0; i < 10; i++) { + for (int j = 0; j < 10; j++) { + for (int k = 0; k < 35; k++) { + for (int t = 0; t < 256; t++) buffer[t] = t; + char *dest = buffer + i + 128; + char *src = buffer+j; + //printf("%d, %d, %d\n", i, j, k); + assert(memcpy(dest, src, k) == dest); + assert(memcmp(dest, src, k) == 0); + } + } + } + printf("ok.\n"); + return 1; + } + ''' + self.do_run(src, 'ok.'); + + def test_getopt(self): + if self.emcc_args is None: return self.skip('needs emcc for libc') + + src = ''' + #pragma clang diagnostic ignored "-Winvalid-pp-token" + #include + #include + #include + + int + main(int argc, char *argv[]) + { + int flags, opt; + int nsecs, tfnd; + + nsecs = 0; + tfnd = 0; + flags = 0; + while ((opt = getopt(argc, argv, "nt:")) != -1) { + switch (opt) { + case 'n': + flags = 1; + break; + case 't': + nsecs = atoi(optarg); + tfnd = 1; + break; + default: /* '?' */ + fprintf(stderr, "Usage: %s [-t nsecs] [-n] name\\n", + argv[0]); + exit(EXIT_FAILURE); + } + } + + printf("flags=%d; tfnd=%d; optind=%d\\n", flags, tfnd, optind); + + if (optind >= argc) { + fprintf(stderr, "Expected argument after options\\n"); + exit(EXIT_FAILURE); + } + + printf("name argument = %s\\n", argv[optind]); + + /* Other code omitted */ + + exit(EXIT_SUCCESS); + } + ''' + self.do_run(src, 'flags=1; tfnd=1; optind=4\nname argument = foobar', args=['-t', '12', '-n', 'foobar']) + + def test_getopt_long(self): + if self.emcc_args is None: return self.skip('needs emcc for libc') + + src = ''' + #pragma clang diagnostic ignored "-Winvalid-pp-token" + #pragma clang diagnostic ignored "-Wdeprecated-writable-strings" + #include /* for printf */ + #include /* for exit */ + #include + + int + main(int argc, char **argv) + { + int c; + int digit_optind = 0; + + while (1) { + int this_option_optind = optind ? optind : 1; + int option_index = 0; + static struct option long_options[] = { + {"add", required_argument, 0, 0 }, + {"append", no_argument, 0, 0 }, + {"delete", required_argument, 0, 0 }, + {"verbose", no_argument, 0, 0 }, + {"create", required_argument, 0, 'c'}, + {"file", required_argument, 0, 0 }, + {0, 0, 0, 0 } + }; + + c = getopt_long(argc, argv, "abc:d:012", + long_options, &option_index); + if (c == -1) + break; + + switch (c) { + case 0: + printf("option %s", long_options[option_index].name); + if (optarg) + printf(" with arg %s", optarg); + printf("\\n"); + break; + + case '0': + case '1': + case '2': + if (digit_optind != 0 && digit_optind != this_option_optind) + printf("digits occur in two different argv-elements.\\n"); + digit_optind = this_option_optind; + printf("option %c\\n", c); + break; + + case 'a': + printf("option a\\n"); + break; + + case 'b': + printf("option b\\n"); + break; + + case 'c': + printf("option c with value '%s'\\n", optarg); + break; + + case 'd': + printf("option d with value '%s'\\n", optarg); + break; + + case '?': + break; + + default: + printf("?? getopt returned character code 0%o ??\\n", c); + } + } + + if (optind < argc) { + printf("non-option ARGV-elements: "); + while (optind < argc) + printf("%s ", argv[optind++]); + printf("\\n"); + } + + exit(EXIT_SUCCESS); + } + ''' + self.do_run(src, 'option file with arg foobar\noption b', args=['--file', 'foobar', '-b']) + + def test_memmove(self): + src = ''' + #include + #include + int main() { + char str[] = "memmove can be very useful....!"; + memmove (str+20, str+15, 11); + puts(str); + return 0; + } + ''' + self.do_run(src, 'memmove can be very very useful') + + def test_memmove2(self): + if Settings.USE_TYPED_ARRAYS != 2: return self.skip('need ta2') + + src = r''' + #include + #include + #include + int main() { + int sum = 0; + char buffer[256]; + for (int i = 0; i < 10; i++) { + for (int j = 0; j < 10; j++) { + for (int k = 0; k < 35; k++) { + for (int t = 0; t < 256; t++) buffer[t] = t; + char *dest = buffer + i; + char *src = buffer + j; + if (dest == src) continue; + //printf("%d, %d, %d\n", i, j, k); + assert(memmove(dest, src, k) == dest); + for (int t = 0; t < 256; t++) sum += buffer[t]; + } + } + } + printf("final: %d.\n", sum); + return 1; + } + ''' + self.do_run(src, 'final: -403200.'); + + def test_memmove3(self): + src = ''' + #include + #include + int main() { + char str[] = "memmove can be vvery useful....!"; + memmove(str+15, str+16, 17); + puts(str); + return 0; + } + ''' + self.do_run(src, 'memmove can be very useful....!') + + def test_bsearch(self): + if Settings.QUANTUM_SIZE == 1: return self.skip('Test cannot work with q1') + + src = ''' + #include + #include + + int cmp(const void* key, const void* member) { + return *(int *)key - *(int *)member; + } + + void printResult(int* needle, int* haystack, unsigned int len) { + void *result = bsearch(needle, haystack, len, sizeof(unsigned int), cmp); + + if (result == NULL) { + printf("null\\n"); + } else { + printf("%d\\n", *(unsigned int *)result); + } + } + + int main() { + int a[] = { -2, -1, 0, 6, 7, 9 }; + int b[] = { 0, 1 }; + + /* Find all keys that exist. */ + for(int i = 0; i < 6; i++) { + int val = a[i]; + + printResult(&val, a, 6); + } + + /* Keys that are covered by the range of the array but aren't in + * the array cannot be found. + */ + int v1 = 3; + int v2 = 8; + printResult(&v1, a, 6); + printResult(&v2, a, 6); + + /* Keys outside the range of the array cannot be found. */ + int v3 = -1; + int v4 = 2; + + printResult(&v3, b, 2); + printResult(&v4, b, 2); + + return 0; + } + ''' + + self.do_run(src, '-2\n-1\n0\n6\n7\n9\nnull\nnull\nnull\nnull') + + def test_nestedstructs(self): + src = ''' + #include + #include "emscripten.h" + + struct base { + int x; + float y; + union { + int a; + float b; + }; + char c; + }; + + struct hashtableentry { + int key; + base data; + }; + + struct hashset { + typedef hashtableentry entry; + struct chain { entry elem; chain *next; }; + // struct chainchunk { chain chains[100]; chainchunk *next; }; + }; + + struct hashtable : hashset { + hashtable() { + base *b = NULL; + entry *e = NULL; + chain *c = NULL; + 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*\\n", + sizeof(base), + int(&(b->x)), int(&(b->y)), int(&(b->a)), int(&(b->b)), int(&(b->c)), + sizeof(hashtableentry), + int(&(e->key)), int(&(e->data)), int(&(e->data.x)), int(&(e->data.y)), int(&(e->data.a)), int(&(e->data.b)), int(&(e->data.c)), + sizeof(hashset::chain), + int(&(c->elem)), int(&(c->next)), int(&(c->elem.key)), int(&(c->elem.data)), int(&(c->elem.data.x)), int(&(c->elem.data.y)), int(&(c->elem.data.a)), int(&(c->elem.data.b)), int(&(c->elem.data.c)) + ); + } + }; + + struct B { char buffer[62]; int last; char laster; char laster2; }; + + struct Bits { + unsigned short A : 1; + unsigned short B : 1; + unsigned short C : 1; + unsigned short D : 1; + unsigned short x1 : 1; + unsigned short x2 : 1; + unsigned short x3 : 1; + unsigned short x4 : 1; + }; + + int main() { + hashtable t; + + // Part 2 - the char[] should be compressed, BUT have a padding space at the end so the next + // one is aligned properly. Also handle char; char; etc. properly. + B *b = NULL; + printf("*%d,%d,%d,%d,%d,%d,%d,%d,%d*\\n", int(b), int(&(b->buffer)), int(&(b->buffer[0])), int(&(b->buffer[1])), int(&(b->buffer[2])), + int(&(b->last)), int(&(b->laster)), int(&(b->laster2)), sizeof(B)); + + // Part 3 - bitfields, and small structures + Bits *b2 = NULL; + printf("*%d*\\n", sizeof(Bits)); + + return 0; + } + ''' + if Settings.QUANTUM_SIZE == 1: + # Compressed memory. Note that sizeof() does give the fat sizes, however! + self.do_run(src, '*16,0,1,2,2,3|20,0,1,1,2,3,3,4|24,0,5,0,1,1,2,3,3,4*\n*0,0,0,1,2,62,63,64,72*\n*2*') + else: + # Bloated memory; same layout as C/C++ + self.do_run(src, '*16,0,4,8,8,12|20,0,4,4,8,12,12,16|24,0,20,0,4,4,8,12,12,16*\n*0,0,0,1,2,64,68,69,72*\n*2*') + + def test_runtimelink(self): + return self.skip('shared libs are deprecated') + if Building.LLVM_OPTS: return self.skip('LLVM opts will optimize printf into puts in the parent, and the child will still look for puts') + if Settings.ASM_JS: return self.skip('asm does not support runtime linking') + + main, supp = self.setup_runtimelink_test() + + self.banned_js_engines = [NODE_JS] # node's global scope behaves differently than everything else, needs investigation FIXME + Settings.LINKABLE = 1 + Settings.BUILD_AS_SHARED_LIB = 2 + Settings.NAMED_GLOBALS = 1 + + self.build(supp, self.get_dir(), self.in_dir('supp.cpp')) + shutil.move(self.in_dir('supp.cpp.o.js'), self.in_dir('liblib.so')) + Settings.BUILD_AS_SHARED_LIB = 0 + + Settings.RUNTIME_LINKED_LIBS = ['liblib.so']; + self.do_run(main, 'supp: 54,2\nmain: 56\nsupp see: 543\nmain see: 76\nok.') + + def test_dlfcn_basic(self): + return self.skip('shared libs are deprecated') + if Settings.ASM_JS: return self.skip('TODO: dlopen in asm') + + Settings.NAMED_GLOBALS = 1 + Settings.LINKABLE = 1 + + lib_src = ''' + #include + + class Foo { + public: + Foo() { + printf("Constructing lib object.\\n"); + } + }; + + Foo global; + ''' + dirname = self.get_dir() + filename = os.path.join(dirname, 'liblib.cpp') + Settings.BUILD_AS_SHARED_LIB = 1 + self.build(lib_src, dirname, filename) + shutil.move(filename + '.o.js', os.path.join(dirname, 'liblib.so')) + + src = ''' + #include + #include + + class Bar { + public: + Bar() { + printf("Constructing main object.\\n"); + } + }; + + Bar global; + + int main() { + dlopen("liblib.so", RTLD_NOW); + return 0; + } + ''' + Settings.BUILD_AS_SHARED_LIB = 0 + add_pre_run_and_checks = ''' +def process(filename): + src = open(filename, 'r').read().replace( + '// {{PRE_RUN_ADDITIONS}}', + "FS.createLazyFile('/', 'liblib.so', 'liblib.so', true, false);" + ) + open(filename, 'w').write(src) +''' + self.do_run(src, 'Constructing main object.\nConstructing lib object.\n', + post_build=add_pre_run_and_checks) + + def test_dlfcn_qsort(self): + return self.skip('shared libs are deprecated') + if self.emcc_args is None: return self.skip('requires emcc') + if Settings.ASM_JS: return self.skip('TODO: dlopen in asm') + + Settings.LINKABLE = 1 + Settings.NAMED_GLOBALS = 1 + + if Settings.USE_TYPED_ARRAYS == 2: + Settings.CORRECT_SIGNS = 1 # Needed for unsafe optimizations + + lib_src = ''' + int lib_cmp(const void* left, const void* right) { + const int* a = (const int*) left; + const int* b = (const int*) right; + if(*a > *b) return 1; + else if(*a == *b) return 0; + else return -1; + } + + typedef int (*CMP_TYPE)(const void*, const void*); + + extern "C" CMP_TYPE get_cmp() { + return lib_cmp; + } + ''' + dirname = self.get_dir() + filename = os.path.join(dirname, 'liblib.cpp') + Settings.BUILD_AS_SHARED_LIB = 1 + Settings.EXPORTED_FUNCTIONS = ['_get_cmp'] + self.build(lib_src, dirname, filename) + shutil.move(filename + '.o.js', os.path.join(dirname, 'liblib.so')) + + src = ''' + #include + #include + #include + + typedef int (*CMP_TYPE)(const void*, const void*); + + int main_cmp(const void* left, const void* right) { + const int* a = (const int*) left; + const int* b = (const int*) right; + if(*a < *b) return 1; + else if(*a == *b) return 0; + else return -1; + } + + int main() { + void* lib_handle; + CMP_TYPE (*getter_ptr)(); + CMP_TYPE lib_cmp_ptr; + int arr[5] = {4, 2, 5, 1, 3}; + + lib_handle = dlopen("liblib.so", RTLD_NOW); + if (lib_handle == NULL) { + printf("Could not load lib.\\n"); + return 1; + } + getter_ptr = (CMP_TYPE (*)()) dlsym(lib_handle, "get_cmp"); + if (getter_ptr == NULL) { + printf("Could not find func.\\n"); + return 1; + } + lib_cmp_ptr = getter_ptr(); + + qsort((void*)arr, 5, sizeof(int), main_cmp); + printf("Sort with main comparison: "); + for (int i = 0; i < 5; i++) { + printf("%d ", arr[i]); + } + printf("\\n"); + + qsort((void*)arr, 5, sizeof(int), lib_cmp_ptr); + printf("Sort with lib comparison: "); + for (int i = 0; i < 5; i++) { + printf("%d ", arr[i]); + } + printf("\\n"); + + return 0; + } + ''' + Settings.BUILD_AS_SHARED_LIB = 0 + Settings.EXPORTED_FUNCTIONS = ['_main'] + add_pre_run_and_checks = ''' +def process(filename): + src = open(filename, 'r').read().replace( + '// {{PRE_RUN_ADDITIONS}}', + "FS.createLazyFile('/', 'liblib.so', 'liblib.so', true, false);" + ) + open(filename, 'w').write(src) +''' + self.do_run(src, 'Sort with main comparison: 5 4 3 2 1 *Sort with lib comparison: 1 2 3 4 5 *', + output_nicerizer=lambda x, err: x.replace('\n', '*'), + post_build=add_pre_run_and_checks) + + def test_dlfcn_data_and_fptr(self): + return self.skip('shared libs are deprecated') + if Settings.ASM_JS: return self.skip('TODO: dlopen in asm') + if Building.LLVM_OPTS: return self.skip('LLVM opts will optimize out parent_func') + + Settings.LINKABLE = 1 + Settings.NAMED_GLOBALS = 1 + + lib_src = ''' + #include + + int global = 42; + + extern void parent_func(); // a function that is defined in the parent + + void lib_fptr() { + printf("Second calling lib_fptr from main.\\n"); + parent_func(); + // call it also through a pointer, to check indexizing + void (*p_f)(); + p_f = parent_func; + p_f(); + } + + extern "C" void (*func(int x, void(*fptr)()))() { + printf("In func: %d\\n", x); + fptr(); + return lib_fptr; + } + ''' + dirname = self.get_dir() + filename = os.path.join(dirname, 'liblib.cpp') + Settings.BUILD_AS_SHARED_LIB = 1 + Settings.EXPORTED_FUNCTIONS = ['_func'] + Settings.EXPORTED_GLOBALS = ['_global'] + self.build(lib_src, dirname, filename) + shutil.move(filename + '.o.js', os.path.join(dirname, 'liblib.so')) + + src = ''' + #include + #include + + typedef void (*FUNCTYPE(int, void(*)()))(); + + FUNCTYPE func; + + void parent_func() { + printf("parent_func called from child\\n"); + } + + void main_fptr() { + printf("First calling main_fptr from lib.\\n"); + } + + int main() { + void* lib_handle; + FUNCTYPE* func_fptr; + + // Test basic lib loading. + lib_handle = dlopen("liblib.so", RTLD_NOW); + if (lib_handle == NULL) { + printf("Could not load lib.\\n"); + return 1; + } + + // Test looked up function. + func_fptr = (FUNCTYPE*) dlsym(lib_handle, "func"); + // Load twice to test cache. + func_fptr = (FUNCTYPE*) dlsym(lib_handle, "func"); + if (func_fptr == NULL) { + printf("Could not find func.\\n"); + return 1; + } + + // Test passing function pointers across module bounds. + void (*fptr)() = func_fptr(13, main_fptr); + fptr(); + + // Test global data. + int* global = (int*) dlsym(lib_handle, "global"); + if (global == NULL) { + printf("Could not find global.\\n"); + return 1; + } + + printf("Var: %d\\n", *global); + + return 0; + } + ''' + Settings.BUILD_AS_SHARED_LIB = 0 + Settings.EXPORTED_FUNCTIONS = ['_main'] + Settings.EXPORTED_GLOBALS = [] + add_pre_run_and_checks = ''' +def process(filename): + src = open(filename, 'r').read().replace( + '// {{PRE_RUN_ADDITIONS}}', + "FS.createLazyFile('/', 'liblib.so', 'liblib.so', true, false);" + ) + open(filename, 'w').write(src) +''' + self.do_run(src, 'In func: 13*First calling main_fptr from lib.*Second calling lib_fptr from main.*parent_func called from child*parent_func called from child*Var: 42*', + output_nicerizer=lambda x, err: x.replace('\n', '*'), + post_build=add_pre_run_and_checks) + + def test_dlfcn_alias(self): + return self.skip('shared libs are deprecated') + if Settings.ASM_JS: return self.skip('TODO: dlopen in asm') + + Settings.LINKABLE = 1 + Settings.NAMED_GLOBALS = 1 + + if Building.LLVM_OPTS == 2: return self.skip('LLVM LTO will optimize away stuff we expect from the shared library') + + lib_src = r''' + #include + extern int parent_global; + extern "C" void func() { + printf("Parent global: %d.\n", parent_global); + } + ''' + dirname = self.get_dir() + filename = os.path.join(dirname, 'liblib.cpp') + Settings.BUILD_AS_SHARED_LIB = 1 + Settings.EXPORTED_FUNCTIONS = ['_func'] + self.build(lib_src, dirname, filename) + shutil.move(filename + '.o.js', os.path.join(dirname, 'liblib.so')) + + src = r''' + #include + + int parent_global = 123; + + int main() { + void* lib_handle; + void (*fptr)(); + + lib_handle = dlopen("liblib.so", RTLD_NOW); + fptr = (void (*)())dlsym(lib_handle, "func"); + fptr(); + parent_global = 456; + fptr(); + + return 0; + } + ''' + Settings.BUILD_AS_SHARED_LIB = 0 + Settings.INCLUDE_FULL_LIBRARY = 1 + Settings.EXPORTED_FUNCTIONS = ['_main'] + add_pre_run_and_checks = ''' +def process(filename): + src = open(filename, 'r').read().replace( + '// {{PRE_RUN_ADDITIONS}}', + "FS.createLazyFile('/', 'liblib.so', 'liblib.so', true, false);" + ) + open(filename, 'w').write(src) +''' + self.do_run(src, 'Parent global: 123.*Parent global: 456.*', + output_nicerizer=lambda x, err: x.replace('\n', '*'), + post_build=add_pre_run_and_checks, + extra_emscripten_args=['-H', 'libc/fcntl.h,libc/sys/unistd.h,poll.h,libc/math.h,libc/time.h,libc/langinfo.h']) + Settings.INCLUDE_FULL_LIBRARY = 0 + + def test_dlfcn_varargs(self): + return self.skip('shared libs are deprecated') + if Settings.ASM_JS: return self.skip('TODO: dlopen in asm') + + Settings.LINKABLE = 1 + Settings.NAMED_GLOBALS = 1 + + if Building.LLVM_OPTS == 2: return self.skip('LLVM LTO will optimize things that prevent shared objects from working') + if Settings.QUANTUM_SIZE == 1: return self.skip('FIXME: Add support for this') + + lib_src = r''' + void print_ints(int n, ...); + extern "C" void func() { + print_ints(2, 13, 42); + } + ''' + dirname = self.get_dir() + filename = os.path.join(dirname, 'liblib.cpp') + Settings.BUILD_AS_SHARED_LIB = 1 + Settings.EXPORTED_FUNCTIONS = ['_func'] + self.build(lib_src, dirname, filename) + shutil.move(filename + '.o.js', os.path.join(dirname, 'liblib.so')) + + src = r''' + #include + #include + #include + + void print_ints(int n, ...) { + va_list args; + va_start(args, n); + for (int i = 0; i < n; i++) { + printf("%d\n", va_arg(args, int)); + } + va_end(args); + } + + int main() { + void* lib_handle; + void (*fptr)(); + + print_ints(2, 100, 200); + + lib_handle = dlopen("liblib.so", RTLD_NOW); + fptr = (void (*)())dlsym(lib_handle, "func"); + fptr(); + + return 0; + } + ''' + Settings.BUILD_AS_SHARED_LIB = 0 + Settings.EXPORTED_FUNCTIONS = ['_main'] + add_pre_run_and_checks = ''' +def process(filename): + src = open(filename, 'r').read().replace( + '// {{PRE_RUN_ADDITIONS}}', + "FS.createLazyFile('/', 'liblib.so', 'liblib.so', true, false);" + ) + open(filename, 'w').write(src) +''' + self.do_run(src, '100\n200\n13\n42\n', + post_build=add_pre_run_and_checks) + + def test_dlfcn_self(self): + if Settings.USE_TYPED_ARRAYS == 1: return self.skip('Does not work with USE_TYPED_ARRAYS=1') + Settings.DLOPEN_SUPPORT = 1 + + src = r''' +#include +#include + +int global = 123; + +extern "C" __attribute__((noinline)) void foo(int x) { +printf("%d\n", x); +} + +extern "C" __attribute__((noinline)) void repeatable() { +void* self = dlopen(NULL, RTLD_LAZY); +int* global_ptr = (int*)dlsym(self, "global"); +void (*foo_ptr)(int) = (void (*)(int))dlsym(self, "foo"); +foo_ptr(*global_ptr); +dlclose(self); +} + +int main() { +repeatable(); +repeatable(); +return 0; +}''' + def post(filename): + with open(filename) as f: + for line in f: + if 'var SYMBOL_TABLE' in line: + table = line + break + else: + raise Exception('Could not find symbol table!') + import json + table = json.loads(table[table.find('{'):table.rfind('}')+1]) + actual = list(sorted(table.keys())) + # ensure there aren't too many globals; we don't want unnamed_addr + assert actual == ['_foo', '_global', '_main', '_repeatable'], \ + "Symbol table does not match: %s" % actual + + self.do_run(src, '123\n123', post_build=(None, post)) + + def test_rand(self): + return self.skip('rand() is now random') # FIXME + + src = r''' + #include + #include + + int main() { + printf("%d\n", rand()); + printf("%d\n", rand()); + + srand(123); + printf("%d\n", rand()); + printf("%d\n", rand()); + srand(123); + printf("%d\n", rand()); + printf("%d\n", rand()); + + unsigned state = 0; + int r; + r = rand_r(&state); + printf("%d, %u\n", r, state); + r = rand_r(&state); + printf("%d, %u\n", r, state); + state = 0; + r = rand_r(&state); + printf("%d, %u\n", r, state); + + return 0; + } + ''' + expected = ''' + 1250496027 + 1116302336 + 440917656 + 1476150784 + 440917656 + 1476150784 + 12345, 12345 + 1406932606, 3554416254 + 12345, 12345 + ''' + self.do_run(src, re.sub(r'(^|\n)\s+', r'\1', expected)) + + def test_strtod(self): + if self.emcc_args is None: return self.skip('needs emcc for libc') + + src = r''' + #include + #include + + int main() { + char* endptr; + + printf("\n"); + printf("%g\n", strtod("0", &endptr)); + printf("%g\n", strtod("0.", &endptr)); + printf("%g\n", strtod("0.0", &endptr)); + printf("%g\n", strtod("-0.0", &endptr)); + printf("%g\n", strtod("1", &endptr)); + printf("%g\n", strtod("1.", &endptr)); + printf("%g\n", strtod("1.0", &endptr)); + printf("%g\n", strtod("z1.0", &endptr)); + printf("%g\n", strtod("0.5", &endptr)); + printf("%g\n", strtod(".5", &endptr)); + printf("%g\n", strtod(".a5", &endptr)); + printf("%g\n", strtod("123", &endptr)); + printf("%g\n", strtod("123.456", &endptr)); + printf("%g\n", strtod("-123.456", &endptr)); + printf("%g\n", strtod("1234567891234567890", &endptr)); + printf("%g\n", strtod("1234567891234567890e+50", &endptr)); + printf("%g\n", strtod("84e+220", &endptr)); + printf("%g\n", strtod("123e-50", &endptr)); + printf("%g\n", strtod("123e-250", &endptr)); + printf("%g\n", strtod("123e-450", &endptr)); + + char str[] = " 12.34e56end"; + printf("%g\n", strtod(str, &endptr)); + printf("%d\n", endptr - str); + printf("%g\n", strtod("84e+420", &endptr)); + + printf("%.12f\n", strtod("1.2345678900000000e+08", NULL)); + + return 0; + } + ''' + expected = ''' + 0 + 0 + 0 + -0 + 1 + 1 + 1 + 0 + 0.5 + 0.5 + 0 + 123 + 123.456 + -123.456 + 1.23457e+18 + 1.23457e+68 + 8.4e+221 + 1.23e-48 + 1.23e-248 + 0 + 1.234e+57 + 10 + inf + 123456789.000000000000 + ''' + + self.do_run(src, re.sub(r'\n\s+', '\n', expected)) + self.do_run(src.replace('strtod', 'strtold'), re.sub(r'\n\s+', '\n', expected)) # XXX add real support for long double + + def test_strtok(self): + src = r''' + #include + #include + + int main() { + char test[80], blah[80]; + char *sep = "\\/:;=-"; + char *word, *phrase, *brkt, *brkb; + + strcpy(test, "This;is.a:test:of=the/string\\tokenizer-function."); + + for (word = strtok_r(test, sep, &brkt); word; word = strtok_r(NULL, sep, &brkt)) { + strcpy(blah, "blah:blat:blab:blag"); + for (phrase = strtok_r(blah, sep, &brkb); phrase; phrase = strtok_r(NULL, sep, &brkb)) { + printf("at %s:%s\n", word, phrase); + } + } + return 0; + } + ''' + + expected = '''at This:blah +at This:blat +at This:blab +at This:blag +at is.a:blah +at is.a:blat +at is.a:blab +at is.a:blag +at test:blah +at test:blat +at test:blab +at test:blag +at of:blah +at of:blat +at of:blab +at of:blag +at the:blah +at the:blat +at the:blab +at the:blag +at string:blah +at string:blat +at string:blab +at string:blag +at tokenizer:blah +at tokenizer:blat +at tokenizer:blab +at tokenizer:blag +at function.:blah +at function.:blat +at function.:blab +at function.:blag +''' + self.do_run(src, expected) + + def test_parseInt(self): + if Settings.USE_TYPED_ARRAYS != 2: return self.skip('i64 mode 1 requires ta2') + if Settings.QUANTUM_SIZE == 1: return self.skip('Q1 and I64_1 do not mix well yet') + src = open(path_from_root('tests', 'parseInt', 'src.c'), 'r').read() + expected = open(path_from_root('tests', 'parseInt', 'output.txt'), 'r').read() + self.do_run(src, expected) + + def test_transtrcase(self): + src = ''' + #include + #include + int main() { + char szToupr[] = "hello, "; + char szTolwr[] = "EMSCRIPTEN"; + strupr(szToupr); + strlwr(szTolwr); + printf(szToupr); + printf(szTolwr); + return 0; + } + ''' + self.do_run(src, 'HELLO, emscripten') + + def test_printf(self): + if Settings.USE_TYPED_ARRAYS != 2: return self.skip('i64 mode 1 requires ta2') + self.banned_js_engines = [NODE_JS, V8_ENGINE] # SpiderMonkey and V8 do different things to float64 typed arrays, un-NaNing, etc. + src = open(path_from_root('tests', 'printf', 'test.c'), 'r').read() + expected = [open(path_from_root('tests', 'printf', 'output.txt'), 'r').read(), + open(path_from_root('tests', 'printf', 'output_i64_1.txt'), 'r').read()] + self.do_run(src, expected) + + def test_printf_2(self): + src = r''' + #include + + int main() { + char c = '1'; + short s = 2; + int i = 3; + long long l = 4; + float f = 5.5; + double d = 6.6; + + printf("%c,%hd,%d,%lld,%.1f,%.1llf\n", c, s, i, l, f, d); + printf("%#x,%#x\n", 1, 0); + + return 0; + } + ''' + self.do_run(src, '1,2,3,4,5.5,6.6\n0x1,0\n') + + def test_vprintf(self): + src = r''' + #include + #include + + void print(char* format, ...) { + va_list args; + va_start (args, format); + vprintf (format, args); + va_end (args); + } + + int main () { + print("Call with %d variable argument.\n", 1); + print("Call with %d variable %s.\n", 2, "arguments"); + + return 0; + } + ''' + expected = ''' + Call with 1 variable argument. + Call with 2 variable arguments. + ''' + self.do_run(src, re.sub('(^|\n)\s+', '\\1', expected)) + + def test_vsnprintf(self): + if self.emcc_args is None: return self.skip('needs i64 math') + + src = r''' + #include + #include + #include + + void printy(const char *f, ...) + { + char buffer[256]; + va_list args; + va_start(args, f); + vsnprintf(buffer, 256, f, args); + puts(buffer); + va_end(args); + } + + int main(int argc, char **argv) { + int64_t x = argc - 1; + int64_t y = argc - 1 + 0x400000; + if (x % 3 == 2) y *= 2; + + printy("0x%llx_0x%llx", x, y); + printy("0x%llx_0x%llx", x, x); + printy("0x%llx_0x%llx", y, x); + printy("0x%llx_0x%llx", y, y); + + { + uint64_t A = 0x800000; + uint64_t B = 0x800000000000ULL; + printy("0x%llx_0x%llx", A, B); + } + { + uint64_t A = 0x800; + uint64_t B = 0x12340000000000ULL; + printy("0x%llx_0x%llx", A, B); + } + { + uint64_t A = 0x000009182746756; + uint64_t B = 0x192837465631ACBDULL; + printy("0x%llx_0x%llx", A, B); + } + + return 0; + } + ''' + self.do_run(src, '''0x0_0x400000 +0x0_0x0 +0x400000_0x0 +0x400000_0x400000 +0x800000_0x800000000000 +0x800_0x12340000000000 +0x9182746756_0x192837465631acbd +''') + + def test_printf_more(self): + src = r''' + #include + int main() { + int size = snprintf(NULL, 0, "%s %d %.2f\n", "me and myself", 25, 1.345); + char buf[size]; + snprintf(buf, size, "%s %d %.2f\n", "me and myself", 25, 1.345); + printf("%d : %s\n", size, buf); + char *buff = NULL; + asprintf(&buff, "%d waka %d\n", 21, 95); + puts(buff); + return 0; + } + ''' + self.do_run(src, '22 : me and myself 25 1.34\n21 waka 95\n') + + def test_perrar(self): + src = r''' + #include + #include + #include + #include + + int main( int argc, char** argv ){ + int retval = open( "NonExistingFile", O_RDONLY ); + if( retval == -1 ) + perror( "Cannot open NonExistingFile" ); + return 0; + } + ''' + self.do_run(src, 'Cannot open NonExistingFile: No such file or directory\n') + + def test_atoX(self): + if self.emcc_args is None: return self.skip('requires ta2') + + src = r''' + #include + #include + + int main () { + printf("%d*", atoi("")); + printf("%d*", atoi("a")); + printf("%d*", atoi(" b")); + printf("%d*", atoi(" c ")); + printf("%d*", atoi("6")); + printf("%d*", atoi(" 5")); + printf("%d*", atoi("4 ")); + printf("%d*", atoi("3 6")); + printf("%d*", atoi(" 3 7")); + printf("%d*", atoi("9 d")); + printf("%d\n", atoi(" 8 e")); + printf("%d*", atol("")); + printf("%d*", atol("a")); + printf("%d*", atol(" b")); + printf("%d*", atol(" c ")); + printf("%d*", atol("6")); + printf("%d*", atol(" 5")); + printf("%d*", atol("4 ")); + printf("%d*", atol("3 6")); + printf("%d*", atol(" 3 7")); + printf("%d*", atol("9 d")); + printf("%d\n", atol(" 8 e")); + printf("%lld*", atoll("6294967296")); + printf("%lld*", atoll("")); + printf("%lld*", atoll("a")); + printf("%lld*", atoll(" b")); + printf("%lld*", atoll(" c ")); + printf("%lld*", atoll("6")); + printf("%lld*", atoll(" 5")); + printf("%lld*", atoll("4 ")); + printf("%lld*", atoll("3 6")); + printf("%lld*", atoll(" 3 7")); + printf("%lld*", atoll("9 d")); + printf("%lld\n", atoll(" 8 e")); + return 0; + } + ''' + self.do_run(src, '0*0*0*0*6*5*4*3*3*9*8\n0*0*0*0*6*5*4*3*3*9*8\n6294967296*0*0*0*0*6*5*4*3*3*9*8\n') + + def test_strstr(self): + src = r''' + #include + #include + + int main() + { + printf("%d\n", !!strstr("\\n", "\\n")); + printf("%d\n", !!strstr("cheezy", "ez")); + printf("%d\n", !!strstr("cheeezy", "ez")); + printf("%d\n", !!strstr("cheeeeeeeeeezy", "ez")); + printf("%d\n", !!strstr("cheeeeeeeeee1zy", "ez")); + printf("%d\n", !!strstr("che1ezy", "ez")); + printf("%d\n", !!strstr("che1ezy", "che")); + printf("%d\n", !!strstr("ce1ezy", "che")); + printf("%d\n", !!strstr("ce1ezy", "ezy")); + printf("%d\n", !!strstr("ce1ezyt", "ezy")); + printf("%d\n", !!strstr("ce1ez1y", "ezy")); + printf("%d\n", !!strstr("cheezy", "a")); + printf("%d\n", !!strstr("cheezy", "b")); + printf("%d\n", !!strstr("cheezy", "c")); + printf("%d\n", !!strstr("cheezy", "d")); + printf("%d\n", !!strstr("cheezy", "g")); + printf("%d\n", !!strstr("cheezy", "h")); + printf("%d\n", !!strstr("cheezy", "i")); + printf("%d\n", !!strstr("cheezy", "e")); + printf("%d\n", !!strstr("cheezy", "x")); + printf("%d\n", !!strstr("cheezy", "y")); + printf("%d\n", !!strstr("cheezy", "z")); + printf("%d\n", !!strstr("cheezy", "_")); + + const char *str = "a big string"; + printf("%d\n", strstr(str, "big") - str); + return 0; + } + ''' + self.do_run(src, '''1 +1 +1 +1 +0 +1 +1 +0 +1 +1 +0 +0 +0 +1 +0 +0 +1 +0 +1 +0 +1 +1 +0 +2 +''') + + def test_sscanf(self): + if self.emcc_args is None: return self.skip('needs emcc for libc') + + src = r''' + #include + #include + #include + + int main () { + #define CHECK(str) \ + { \ + char name[1000]; \ + memset(name, 0, 1000); \ + int prio = 99; \ + sscanf(str, "%s %d", name, &prio); \ + printf("%s : %d\n", name, prio); \ + } + CHECK("en-us 2"); + CHECK("en-r"); + CHECK("en 3"); + + printf("%f, %f\n", atof("1.234567"), atof("cheez")); + + char float_formats[] = "fegE"; + char format[] = "%_"; + for(int i = 0; i < 4; ++i) { + format[1] = float_formats[i]; + + float n = -1; + sscanf(" 2.8208", format, &n); + printf("%.4f\n", n); + + float a = -1; + sscanf("-3.03", format, &a); + printf("%.4f\n", a); + } + + char buffy[100]; + sscanf("cheez some thing moar 123\nyet more\n", "cheez %s", buffy); + printf("|%s|\n", buffy); + sscanf("cheez something\nmoar 123\nyet more\n", "cheez %s", buffy); + printf("|%s|\n", buffy); + sscanf("cheez somethingmoar\tyet more\n", "cheez %s", buffy); + printf("|%s|\n", buffy); + + int numverts = -1; + printf("%d\n", sscanf(" numverts 1499\n", " numverts %d", &numverts)); // white space is the same, even if tab vs space + printf("%d\n", numverts); + + int index; + float u, v; + short start, count; + printf("%d\n", sscanf(" vert 87 ( 0.481565 0.059481 ) 0 1\n", " vert %d ( %f %f ) %hu %hu", &index, &u, &v, &start, &count)); + printf("%d,%.6f,%.6f,%hu,%hu\n", index, u, v, start, count); + + int neg, neg2, neg3 = 0; + printf("%d\n", sscanf("-123 -765 -34-6", "%d %u %d", &neg, &neg2, &neg3)); + printf("%d,%u,%d\n", neg, neg2, neg3); + + { + int a = 0; + sscanf("1", "%i", &a); + printf("%i\n", a); + } + + return 0; + } + ''' + self.do_run(src, 'en-us : 2\nen-r : 99\nen : 3\n1.234567, 0.000000\n2.8208\n-3.0300\n2.8208\n-3.0300\n2.8208\n-3.0300\n2.8208\n-3.0300\n|some|\n|something|\n|somethingmoar|\n' + + '1\n1499\n' + + '5\n87,0.481565,0.059481,0,1\n' + + '3\n-123,4294966531,-34\n' + + '1\n') + + def test_sscanf_2(self): + # doubles + if Settings.USE_TYPED_ARRAYS == 2: + for ftype in ['float', 'double']: + src = r''' + #include + + int main(){ + char strval1[] = "1.2345678901"; + char strval2[] = "1.23456789e5"; + char strval3[] = "1.23456789E5"; + char strval4[] = "1.2345678e-5"; + char strval5[] = "1.2345678E-5"; + double dblval = 1.2345678901; + double tstval; + + sscanf(strval1, "%lf", &tstval); + if(dblval != tstval) printf("FAIL: Values are not equal: %lf %lf\n", dblval, tstval); + else printf("Pass: %lf %lf\n", tstval, dblval); + + sscanf(strval2, "%lf", &tstval); + dblval = 123456.789; + if(dblval != tstval) printf("FAIL: Values are not equal: %lf %lf\n", dblval, tstval); + else printf("Pass: %lf %lf\n", tstval, dblval); + + sscanf(strval3, "%lf", &tstval); + dblval = 123456.789; + if(dblval != tstval) printf("FAIL: Values are not equal: %lf %lf\n", dblval, tstval); + else printf("Pass: %lf %lf\n", tstval, dblval); + + sscanf(strval4, "%lf", &tstval); + dblval = 0.000012345678; + if(dblval != tstval) printf("FAIL: Values are not equal: %lf %lf\n", dblval, tstval); + else printf("Pass: %lf %lf\n", tstval, dblval); + + sscanf(strval5, "%lf", &tstval); + dblval = 0.000012345678; + if(dblval != tstval) printf("FAIL: Values are not equal: %lf %lf\n", dblval, tstval); + else printf("Pass: %lf %lf\n", tstval, dblval); + + return 0; + } + ''' + if ftype == 'float': + self.do_run(src.replace('%lf', '%f').replace('double', 'float'), '''Pass: 1.234568 1.234568 +Pass: 123456.789063 123456.789063 +Pass: 123456.789063 123456.789063 +Pass: 0.000012 0.000012 +Pass: 0.000012 0.000012''') + else: + self.do_run(src, '''Pass: 1.234568 1.234568 +Pass: 123456.789000 123456.789000 +Pass: 123456.789000 123456.789000 +Pass: 0.000012 0.000012 +Pass: 0.000012 0.000012''') + + def test_sscanf_n(self): + src = r''' + #include + int main() { + char *line = "version 1.0"; + int i, l, lineno; + char word[80]; + if (sscanf(line, "%s%n", word, &l) != 1) { + printf("Header format error, line %d\n", lineno); + } + printf("[DEBUG] word 1: %s, l: %d\n", word, l); + + int x = sscanf("one %n two", "%s %n", word, &l); + printf("%d,%s,%d\n", x, word, l); + { + int a, b, c, count; + count = sscanf("12345 6789", "%d %n%d", &a, &b, &c); + printf("%i %i %i %i\n", count, a, b, c); + } + return 0; + } + ''' + self.do_run(src, '''[DEBUG] word 1: version, l: 7\n1,one,4\n2 12345 6 6789\n''') + + def test_sscanf_whitespace(self): + src = r''' + #include + + int main() { + short int x; + short int y; + + const char* buffer[] = { + "173,16", + " 16,173", + "183, 173", + " 17, 287", + " 98, 123, " + }; + + for (int i=0; i<5; ++i) { + sscanf(buffer[i], "%hd,%hd", &x, &y); + printf("%d:%d,%d ", i, x, y); + } + + return 0; + } + ''' + self.do_run(src, '''0:173,16 1:16,173 2:183,173 3:17,287 4:98,123''') + + def test_sscanf_other_whitespace(self): + Settings.SAFE_HEAP = 0 # use i16s in printf + + src = r''' + #include + + int main() { + short int x; + short int y; + + const char* buffer[] = { + "\t2\t3\t", /* TAB - horizontal tab */ + "\t\t5\t\t7\t\t", + "\n11\n13\n", /* LF - line feed */ + "\n\n17\n\n19\n\n", + "\v23\v29\v", /* VT - vertical tab */ + "\v\v31\v\v37\v\v", + "\f41\f43\f", /* FF - form feed */ + "\f\f47\f\f53\f\f", + "\r59\r61\r", /* CR - carrage return */ + "\r\r67\r\r71\r\r" + }; + + for (int i=0; i<10; ++i) { + x = 0; y = 0; + sscanf(buffer[i], " %d %d ", &x, &y); + printf("%d, %d, ", x, y); + } + + return 0; + } + ''' + self.do_run(src, '''2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, ''') + + def test_sscanf_3(self): + # i64 + if not Settings.USE_TYPED_ARRAYS == 2: return self.skip('64-bit sscanf only supported in ta2') + src = r''' + #include + #include + + int main(){ + + int64_t s, m, l; + printf("%d\n", sscanf("123 1073741823 1125899906842620", "%lld %lld %lld", &s, &m, &l)); + printf("%lld,%lld,%lld\n", s, m, l); + + int64_t negS, negM, negL; + printf("%d\n", sscanf("-123 -1073741823 -1125899906842620", "%lld %lld %lld", &negS, &negM, &negL)); + printf("%lld,%lld,%lld\n", negS, negM, negL); + + return 0; + } + ''' + + self.do_run(src, '3\n123,1073741823,1125899906842620\n' + + '3\n-123,-1073741823,-1125899906842620\n') + + def test_sscanf_4(self): + src = r''' + #include + + int main() + { + char pYear[16], pMonth[16], pDay[16], pDate[64]; + printf("%d\n", sscanf("Nov 19 2012", "%s%s%s", pMonth, pDay, pYear)); + printf("day %s, month %s, year %s \n", pDay, pMonth, pYear); + return(0); + } + ''' + self.do_run(src, '3\nday 19, month Nov, year 2012'); + + def test_sscanf_5(self): + src = r''' + #include "stdio.h" + + static const char *colors[] = { + " c black", + ". c #001100", + "X c #111100" + }; + + int main(){ + unsigned char code; + char color[32]; + int rcode; + for(int i = 0; i < 3; i++) { + rcode = sscanf(colors[i], "%c c %s", &code, color); + printf("%i, %c, %s\n", rcode, code, color); + } + } + ''' + self.do_run(src, '2, , black\n2, ., #001100\n2, X, #111100'); + + def test_sscanf_6(self): + src = r''' + #include + #include + int main() + { + char *date = "18.07.2013w"; + char c[10]; + memset(c, 0, 10); + int y, m, d, i; + i = sscanf(date, "%d.%d.%4d%c", &d, &m, &y, c); + printf("date: %s; day %2d, month %2d, year %4d, extra: %c, %d\n", date, d, m, y, c[0], i); + i = sscanf(date, "%d.%d.%3c", &d, &m, c); + printf("date: %s; day %2d, month %2d, year %4d, extra: %s, %d\n", date, d, m, y, c, i); + } + ''' + self.do_run(src, '''date: 18.07.2013w; day 18, month 7, year 2013, extra: w, 4 +date: 18.07.2013w; day 18, month 7, year 2013, extra: 201, 3 +'''); + + def test_sscanf_skip(self): + if Settings.USE_TYPED_ARRAYS != 2: return self.skip("need ta2 for full i64") + + src = r''' + #include + #include + + int main(){ + int val1; + printf("%d\n", sscanf("10 20 30 40", "%*lld %*d %d", &val1)); + printf("%d\n", val1); + + int64_t large, val2; + printf("%d\n", sscanf("1000000 -1125899906842620 -123 -1073741823", "%lld %*lld %ld %*d", &large, &val2)); + printf("%lld,%d\n", large, val2); + + return 0; + } + ''' + self.do_run(src, '1\n30\n2\n1000000,-123\n') + + def test_sscanf_caps(self): + src = r''' + #include "stdio.h" + + int main(){ + unsigned int a; + float e, f, g; + sscanf("a 1.1 1.1 1.1", "%X %E %F %G", &a, &e, &f, &g); + printf("%d %.1F %.1F %.1F\n", a, e, f, g); + } + ''' + self.do_run(src, '10 1.1 1.1 1.1'); + + def test_langinfo(self): + src = open(path_from_root('tests', 'langinfo', 'test.c'), 'r').read() + expected = open(path_from_root('tests', 'langinfo', 'output.txt'), 'r').read() + self.do_run(src, expected, extra_emscripten_args=['-H', 'libc/langinfo.h']) + + def test_files(self): + if self.emcc_args is not None and '-O2' in self.emcc_args: + self.emcc_args += ['--closure', '1'] # Use closure here, to test we don't break FS stuff + + Settings.CORRECT_SIGNS = 1 # Just so our output is what we expect. Can flip them both. + post = ''' +def process(filename): + src = \'\'\' + var Module = { + 'noFSInit': true, + 'preRun': function() { + FS.createLazyFile('/', 'test.file', 'test.file', true, false); + // Test FS_* exporting + Module['FS_createDataFile']('/', 'somefile.binary', [100, 200, 50, 25, 10, 77, 123], true, false); // 200 becomes -56, since signed chars are used in memory + var test_files_input = 'hi there!'; + var test_files_input_index = 0; + FS.init(function() { + return test_files_input.charCodeAt(test_files_input_index++) || null; + }); + } + }; + \'\'\' + open(filename, 'r').read() + open(filename, 'w').write(src) +''' + other = open(os.path.join(self.get_dir(), 'test.file'), 'w') + other.write('some data'); + other.close() + + src = open(path_from_root('tests', 'files.cpp'), 'r').read() + self.do_run(src, ('size: 7\ndata: 100,-56,50,25,10,77,123\nloop: 100 -56 50 25 10 77 123 \ninput:hi there!\ntexto\n$\n5 : 10,30,20,11,88\nother=some data.\nseeked=me da.\nseeked=ata.\nseeked=ta.\nfscanfed: 10 - hello\nok.\ntexte\n', 'size: 7\ndata: 100,-56,50,25,10,77,123\nloop: 100 -56 50 25 10 77 123 \ninput:hi there!\ntexto\ntexte\n$\n5 : 10,30,20,11,88\nother=some data.\nseeked=me da.\nseeked=ata.\nseeked=ta.\nfscanfed: 10 - hello\nok.\n'), + post_build=post, extra_emscripten_args=['-H', 'libc/fcntl.h']) + + def test_files_m(self): + # Test for Module.stdin etc. + + Settings.CORRECT_SIGNS = 1 + + post = ''' +def process(filename): + src = \'\'\' + var data = [10, 20, 40, 30]; + var Module = { + stdin: function() { return data.pop() || null }, + stdout: function(x) { Module.print('got: ' + x) } + }; + \'\'\' + open(filename, 'r').read() + open(filename, 'w').write(src) +''' + src = r''' + #include + #include + + int main () { + char c; + fprintf(stderr, "isatty? %d,%d,%d\n", isatty(fileno(stdin)), isatty(fileno(stdout)), isatty(fileno(stderr))); + while ((c = fgetc(stdin)) != EOF) { + putc(c+5, stdout); + } + return 0; + } + ''' + self.do_run(src, ('got: 35\ngot: 45\ngot: 25\ngot: 15\nisatty? 0,0,1\n', 'isatty? 0,0,1\ngot: 35\ngot: 45\ngot: 25\ngot: 15\n'), post_build=post) + + def test_fwrite_0(self): + src = r''' + #include + #include + + int main () + { + FILE *fh; + + fh = fopen("a.txt", "wb"); + if (!fh) exit(1); + fclose(fh); + + fh = fopen("a.txt", "rb"); + if (!fh) exit(1); + + char data[] = "foobar"; + size_t written = fwrite(data, 1, sizeof(data), fh); + + printf("written=%zu\n", written); + } + ''' + self.do_run(src, 'written=0') + + def test_fgetc_ungetc(self): + src = open(path_from_root('tests', 'stdio', 'test_fgetc_ungetc.c'), 'r').read() + self.do_run(src, 'success', force_c=True) + + def test_fgetc_unsigned(self): + if self.emcc_args is None: return self.skip('requires emcc') + src = r''' + #include + int main() { + FILE *file = fopen("file_with_byte_234.txt", "rb"); + int c = fgetc(file); + printf("*%d\n", c); + } + ''' + open('file_with_byte_234.txt', 'wb').write('\xea') + self.emcc_args += ['--embed-file', 'file_with_byte_234.txt'] + self.do_run(src, '*234\n') + + def test_fgets_eol(self): + if self.emcc_args is None: return self.skip('requires emcc') + src = r''' + #include + char buf[32]; + int main() + { + char *r = "SUCCESS"; + FILE *f = fopen("eol.txt", "r"); + while (fgets(buf, 32, f) != NULL) { + if (buf[0] == '\0') { + r = "FAIL"; + break; + } + } + printf("%s\n", r); + fclose(f); + return 0; + } + ''' + open('eol.txt', 'wb').write('\n') + self.emcc_args += ['--embed-file', 'eol.txt'] + self.do_run(src, 'SUCCESS\n') + + def test_fscanf(self): + if self.emcc_args is None: return self.skip('requires emcc') + open(os.path.join(self.get_dir(), 'three_numbers.txt'), 'w').write('''-1 0.1 -.1''') + src = r''' + #include + #include + #include + int main() + { + float x = FLT_MAX, y = FLT_MAX, z = FLT_MAX; + + FILE* fp = fopen("three_numbers.txt", "r"); + if (fp) { + int match = fscanf(fp, " %f %f %f ", &x, &y, &z); + printf("match = %d\n", match); + printf("x = %0.1f, y = %0.1f, z = %0.1f\n", x, y, z); + } else { + printf("failed to open three_numbers.txt\n"); + } + return 0; + } + ''' + self.emcc_args += ['--embed-file', 'three_numbers.txt'] + self.do_run(src, 'match = 3\nx = -1.0, y = 0.1, z = -0.1\n') + + def test_readdir(self): + src = open(path_from_root('tests', 'dirent', 'test_readdir.c'), 'r').read() + self.do_run(src, 'success', force_c=True) + + def test_stat(self): + src = open(path_from_root('tests', 'stat', 'test_stat.c'), 'r').read() + self.do_run(src, 'success', force_c=True) + + def test_stat_chmod(self): + src = open(path_from_root('tests', 'stat', 'test_chmod.c'), 'r').read() + self.do_run(src, 'success', force_c=True) + + def test_stat_mknod(self): + src = open(path_from_root('tests', 'stat', 'test_mknod.c'), 'r').read() + self.do_run(src, 'success', force_c=True) + + def test_fcntl(self): + add_pre_run = ''' +def process(filename): + src = open(filename, 'r').read().replace( + '// {{PRE_RUN_ADDITIONS}}', + "FS.createDataFile('/', 'test', 'abcdef', true, true);" + ) + open(filename, 'w').write(src) +''' + src = open(path_from_root('tests', 'fcntl', 'src.c'), 'r').read() + expected = open(path_from_root('tests', 'fcntl', 'output.txt'), 'r').read() + self.do_run(src, expected, post_build=add_pre_run, extra_emscripten_args=['-H', 'libc/fcntl.h']) + + def test_fcntl_open(self): + src = open(path_from_root('tests', 'fcntl-open', 'src.c'), 'r').read() + expected = open(path_from_root('tests', 'fcntl-open', 'output.txt'), 'r').read() + self.do_run(src, expected, force_c=True, extra_emscripten_args=['-H', 'libc/fcntl.h']) + + def test_fcntl_misc(self): + add_pre_run = ''' +def process(filename): + src = open(filename, 'r').read().replace( + '// {{PRE_RUN_ADDITIONS}}', + "FS.createDataFile('/', 'test', 'abcdef', true, true);" + ) + open(filename, 'w').write(src) +''' + src = open(path_from_root('tests', 'fcntl-misc', 'src.c'), 'r').read() + expected = open(path_from_root('tests', 'fcntl-misc', 'output.txt'), 'r').read() + self.do_run(src, expected, post_build=add_pre_run, extra_emscripten_args=['-H', 'libc/fcntl.h']) + + def test_poll(self): + add_pre_run = ''' +def process(filename): + src = open(filename, 'r').read().replace( + '// {{PRE_RUN_ADDITIONS}}', + \'\'\' + FS.createDataFile('/', 'file', 'abcdef', true, true); + FS.createDevice('/', 'device', function() {}, function() {}); + \'\'\' + ) + open(filename, 'w').write(src) +''' + src = r''' + #include + #include + #include + #include + + int main() { + struct pollfd multi[5]; + multi[0].fd = open("/file", O_RDONLY, 0777); + multi[1].fd = open("/device", O_RDONLY, 0777); + multi[2].fd = 123; + multi[3].fd = open("/file", O_RDONLY, 0777); + multi[4].fd = open("/file", O_RDONLY, 0777); + multi[0].events = POLLIN | POLLOUT | POLLNVAL | POLLERR; + multi[1].events = POLLIN | POLLOUT | POLLNVAL | POLLERR; + multi[2].events = POLLIN | POLLOUT | POLLNVAL | POLLERR; + multi[3].events = 0x00; + multi[4].events = POLLOUT | POLLNVAL | POLLERR; + + printf("ret: %d\n", poll(multi, 5, 123)); + printf("errno: %d\n", errno); + printf("multi[0].revents: %d\n", multi[0].revents == (POLLIN | POLLOUT)); + printf("multi[1].revents: %d\n", multi[1].revents == (POLLIN | POLLOUT)); + printf("multi[2].revents: %d\n", multi[2].revents == POLLNVAL); + printf("multi[3].revents: %d\n", multi[3].revents == 0); + printf("multi[4].revents: %d\n", multi[4].revents == POLLOUT); + + return 0; + } + ''' + expected = r''' + ret: 4 + errno: 0 + multi[0].revents: 1 + multi[1].revents: 1 + multi[2].revents: 1 + multi[3].revents: 1 + multi[4].revents: 1 + ''' + self.do_run(src, re.sub('(^|\n)\s+', '\\1', expected), post_build=add_pre_run, extra_emscripten_args=['-H', 'libc/fcntl.h,poll.h']) + + def test_statvfs(self): + src = r''' + #include + #include + #include + + int main() { + struct statvfs s; + + printf("result: %d\n", statvfs("/test", &s)); + printf("errno: %d\n", errno); + + printf("f_bsize: %lu\n", s.f_bsize); + printf("f_frsize: %lu\n", s.f_frsize); + printf("f_blocks: %lu\n", s.f_blocks); + printf("f_bfree: %lu\n", s.f_bfree); + printf("f_bavail: %lu\n", s.f_bavail); + printf("f_files: %d\n", s.f_files > 5); + printf("f_ffree: %lu\n", s.f_ffree); + printf("f_favail: %lu\n", s.f_favail); + printf("f_fsid: %lu\n", s.f_fsid); + printf("f_flag: %lu\n", s.f_flag); + printf("f_namemax: %lu\n", s.f_namemax); + + return 0; + } + ''' + expected = r''' + result: 0 + errno: 0 + f_bsize: 4096 + f_frsize: 4096 + f_blocks: 1000000 + f_bfree: 500000 + f_bavail: 500000 + f_files: 1 + f_ffree: 1000000 + f_favail: 1000000 + f_fsid: 42 + f_flag: 2 + f_namemax: 255 + ''' + self.do_run(src, re.sub('(^|\n)\s+', '\\1', expected)) + + def test_libgen(self): + src = r''' + #include + #include + + int main() { + char p1[16] = "/usr/lib", p1x[16] = "/usr/lib"; + printf("%s -> ", p1); + printf("%s : %s\n", dirname(p1x), basename(p1)); + + char p2[16] = "/usr", p2x[16] = "/usr"; + printf("%s -> ", p2); + printf("%s : %s\n", dirname(p2x), basename(p2)); + + char p3[16] = "/usr/", p3x[16] = "/usr/"; + printf("%s -> ", p3); + printf("%s : %s\n", dirname(p3x), basename(p3)); + + char p4[16] = "/usr/lib///", p4x[16] = "/usr/lib///"; + printf("%s -> ", p4); + printf("%s : %s\n", dirname(p4x), basename(p4)); + + char p5[16] = "/", p5x[16] = "/"; + printf("%s -> ", p5); + printf("%s : %s\n", dirname(p5x), basename(p5)); + + char p6[16] = "///", p6x[16] = "///"; + printf("%s -> ", p6); + printf("%s : %s\n", dirname(p6x), basename(p6)); + + char p7[16] = "/usr/../lib/..", p7x[16] = "/usr/../lib/.."; + printf("%s -> ", p7); + printf("%s : %s\n", dirname(p7x), basename(p7)); + + char p8[16] = "", p8x[16] = ""; + printf("(empty) -> %s : %s\n", dirname(p8x), basename(p8)); + + printf("(null) -> %s : %s\n", dirname(0), basename(0)); + + return 0; + } + ''' + expected = ''' + /usr/lib -> /usr : lib + /usr -> / : usr + /usr/ -> / : usr + /usr/lib/// -> /usr : lib + / -> / : / + /// -> / : / + /usr/../lib/.. -> /usr/../lib : .. + (empty) -> . : . + (null) -> . : . + ''' + self.do_run(src, re.sub('(^|\n)\s+', '\\1', expected)) + + def test_utime(self): + src = open(path_from_root('tests', 'utime', 'test_utime.c'), 'r').read() + self.do_run(src, 'success', force_c=True) + + def test_utf(self): + self.banned_js_engines = [SPIDERMONKEY_ENGINE] # only node handles utf well + Settings.EXPORTED_FUNCTIONS = ['_main', '_malloc'] + + src = r''' + #include + #include + + int main() { + char *c = "μ†ℱ ╋ℯ╳╋"; + printf("%d %d %d %d %s\n", c[0]&0xff, c[1]&0xff, c[2]&0xff, c[3]&0xff, c); + emscripten_run_script("cheez = _malloc(100);" + "Module.writeStringToMemory(\"μ†ℱ ╋ℯ╳╋\", cheez);" + "Module.print([Pointer_stringify(cheez), Module.getValue(cheez, 'i8')&0xff, Module.getValue(cheez+1, 'i8')&0xff, Module.getValue(cheez+2, 'i8')&0xff, Module.getValue(cheez+3, 'i8')&0xff, ]);"); + } + ''' + self.do_run(src, '206 188 226 128 μ†ℱ ╋ℯ╳╋\nμ†ℱ ╋ℯ╳╋,206,188,226,128\n'); + + def test_direct_string_constant_usage(self): + if self.emcc_args is None: return self.skip('requires libcxx') + + src = ''' + #include + template + void printText( const char (&text)[ i ] ) + { + std::cout << text; + } + int main() + { + printText( "some string constant" ); + return 0; + } + ''' + self.do_run(src, "some string constant") + + def test_std_cout_new(self): + if self.emcc_args is None: return self.skip('requires emcc') + + src = ''' + #include + + struct NodeInfo { //structure that we want to transmit to our shaders + float x; + float y; + float s; + float c; + }; + const int nbNodes = 100; + NodeInfo * data = new NodeInfo[nbNodes]; //our data that will be transmitted using float texture. + + template + void printText( const char (&text)[ i ] ) + { + std::cout << text << std::endl; + } + + int main() + { + printText( "some string constant" ); + return 0; + } + ''' + + self.do_run(src, "some string constant") + + def test_istream(self): + if self.emcc_args is None: return self.skip('requires libcxx') + + src = ''' + #include + #include + #include + + int main() + { + std::string mystring("1 2 3"); + std::istringstream is(mystring); + int one, two, three; + + is >> one >> two >> three; + + printf( "%i %i %i", one, two, three ); + } + ''' + for linkable in [0]:#, 1]: + print linkable + Settings.LINKABLE = linkable # regression check for issue #273 + self.do_run(src, "1 2 3") + + def test_fs_base(self): + Settings.INCLUDE_FULL_LIBRARY = 1 + try: + addJS = ''' +def process(filename): + import tools.shared as shared + src = open(filename, 'r').read().replace('FS.init();', '').replace( # Disable normal initialization, replace with ours + '// {{PRE_RUN_ADDITIONS}}', + open(shared.path_from_root('tests', 'filesystem', 'src.js'), 'r').read()) + open(filename, 'w').write(src) +''' + src = 'int main() {return 0;}\n' + expected = open(path_from_root('tests', 'filesystem', 'output.txt'), 'r').read() + self.do_run(src, expected, post_build=addJS, extra_emscripten_args=['-H', 'libc/fcntl.h,libc/sys/unistd.h,poll.h,libc/math.h,libc/langinfo.h,libc/time.h']) + finally: + Settings.INCLUDE_FULL_LIBRARY = 0 + + def test_unistd_access(self): + add_pre_run = ''' +def process(filename): + import tools.shared as shared + src = open(filename, 'r').read().replace( + '// {{PRE_RUN_ADDITIONS}}', + open(shared.path_from_root('tests', 'unistd', 'access.js'), 'r').read() + ) + open(filename, 'w').write(src) +''' + src = open(path_from_root('tests', 'unistd', 'access.c'), 'r').read() + expected = open(path_from_root('tests', 'unistd', 'access.out'), 'r').read() + self.do_run(src, expected, post_build=add_pre_run) + + def test_unistd_curdir(self): + add_pre_run = ''' +def process(filename): + import tools.shared as shared + src = open(filename, 'r').read().replace( + '// {{PRE_RUN_ADDITIONS}}', + open(shared.path_from_root('tests', 'unistd', 'curdir.js'), 'r').read() + ) + open(filename, 'w').write(src) +''' + src = open(path_from_root('tests', 'unistd', 'curdir.c'), 'r').read() + expected = open(path_from_root('tests', 'unistd', 'curdir.out'), 'r').read() + self.do_run(src, expected, post_build=add_pre_run) + + def test_unistd_close(self): + src = open(path_from_root('tests', 'unistd', 'close.c'), 'r').read() + expected = open(path_from_root('tests', 'unistd', 'close.out'), 'r').read() + self.do_run(src, expected) + + def test_unistd_confstr(self): + src = open(path_from_root('tests', 'unistd', 'confstr.c'), 'r').read() + expected = open(path_from_root('tests', 'unistd', 'confstr.out'), 'r').read() + self.do_run(src, expected, extra_emscripten_args=['-H', 'libc/unistd.h']) + + def test_unistd_ttyname(self): + src = open(path_from_root('tests', 'unistd', 'ttyname.c'), 'r').read() + self.do_run(src, 'success', force_c=True) + + def test_unistd_dup(self): + src = open(path_from_root('tests', 'unistd', 'dup.c'), 'r').read() + expected = open(path_from_root('tests', 'unistd', 'dup.out'), 'r').read() + self.do_run(src, expected) + + def test_unistd_pathconf(self): + src = open(path_from_root('tests', 'unistd', 'pathconf.c'), 'r').read() + expected = open(path_from_root('tests', 'unistd', 'pathconf.out'), 'r').read() + self.do_run(src, expected) + + def test_unistd_truncate(self): + add_pre_run = ''' +def process(filename): + import tools.shared as shared + src = open(filename, 'r').read().replace( + '// {{PRE_RUN_ADDITIONS}}', + open(shared.path_from_root('tests', 'unistd', 'truncate.js'), 'r').read() + ) + open(filename, 'w').write(src) +''' + src = open(path_from_root('tests', 'unistd', 'truncate.c'), 'r').read() + expected = open(path_from_root('tests', 'unistd', 'truncate.out'), 'r').read() + self.do_run(src, expected, post_build=add_pre_run) + + def test_unistd_swab(self): + src = open(path_from_root('tests', 'unistd', 'swab.c'), 'r').read() + expected = open(path_from_root('tests', 'unistd', 'swab.out'), 'r').read() + self.do_run(src, expected) + + def test_unistd_isatty(self): + src = open(path_from_root('tests', 'unistd', 'isatty.c'), 'r').read() + self.do_run(src, 'success', force_c=True) + + def test_unistd_sysconf(self): + src = open(path_from_root('tests', 'unistd', 'sysconf.c'), 'r').read() + expected = open(path_from_root('tests', 'unistd', 'sysconf.out'), 'r').read() + self.do_run(src, expected) + + def test_unistd_login(self): + src = open(path_from_root('tests', 'unistd', 'login.c'), 'r').read() + expected = open(path_from_root('tests', 'unistd', 'login.out'), 'r').read() + self.do_run(src, expected) + + def test_unistd_unlink(self): + src = open(path_from_root('tests', 'unistd', 'unlink.c'), 'r').read() + self.do_run(src, 'success', force_c=True) + + def test_unistd_links(self): + add_pre_run = ''' +def process(filename): + import tools.shared as shared + src = open(filename, 'r').read().replace( + '// {{PRE_RUN_ADDITIONS}}', + open(shared.path_from_root('tests', 'unistd', 'links.js'), 'r').read() + ) + open(filename, 'w').write(src) +''' + src = open(path_from_root('tests', 'unistd', 'links.c'), 'r').read() + expected = open(path_from_root('tests', 'unistd', 'links.out'), 'r').read() + self.do_run(src, expected, post_build=add_pre_run) + + def test_unistd_sleep(self): + src = open(path_from_root('tests', 'unistd', 'sleep.c'), 'r').read() + expected = open(path_from_root('tests', 'unistd', 'sleep.out'), 'r').read() + self.do_run(src, expected) + + def test_unistd_io(self): + add_pre_run = ''' +def process(filename): + import tools.shared as shared + src = open(filename, 'r').read().replace( + '// {{PRE_RUN_ADDITIONS}}', + open(shared.path_from_root('tests', 'unistd', 'io.js'), 'r').read() + ) + open(filename, 'w').write(src) +''' + src = open(path_from_root('tests', 'unistd', 'io.c'), 'r').read() + expected = open(path_from_root('tests', 'unistd', 'io.out'), 'r').read() + self.do_run(src, expected, post_build=add_pre_run) + + def test_unistd_misc(self): + src = open(path_from_root('tests', 'unistd', 'misc.c'), 'r').read() + expected = open(path_from_root('tests', 'unistd', 'misc.out'), 'r').read() + self.do_run(src, expected) + + def test_uname(self): + src = r''' + #include + #include + + int main() { + struct utsname u; + printf("ret: %d\n", uname(&u)); + printf("sysname: %s\n", u.sysname); + printf("nodename: %s\n", u.nodename); + printf("release: %s\n", u.release); + printf("version: %s\n", u.version); + printf("machine: %s\n", u.machine); + printf("invalid: %d\n", uname(0)); + return 0; + } + ''' + expected = ''' + ret: 0 + sysname: Emscripten + nodename: emscripten + release: 1.0 + version: #1 + machine: x86-JS + ''' + self.do_run(src, re.sub('(^|\n)\s+', '\\1', expected)) + + def test_env(self): + src = open(path_from_root('tests', 'env', 'src.c'), 'r').read() + expected = open(path_from_root('tests', 'env', 'output.txt'), 'r').read() + self.do_run(src, expected) + + def test_systypes(self): + src = open(path_from_root('tests', 'systypes', 'src.c'), 'r').read() + expected = open(path_from_root('tests', 'systypes', 'output.txt'), 'r').read() + self.do_run(src, expected) + + def test_getloadavg(self): + src = r''' + #include + #include + + int main() { + double load[5] = {42.13, 42.13, 42.13, 42.13, 42.13}; + printf("ret: %d\n", getloadavg(load, 5)); + printf("load[0]: %.3lf\n", load[0]); + printf("load[1]: %.3lf\n", load[1]); + printf("load[2]: %.3lf\n", load[2]); + printf("load[3]: %.3lf\n", load[3]); + printf("load[4]: %.3lf\n", load[4]); + return 0; + } + ''' + expected = ''' + ret: 3 + load[0]: 0.100 + load[1]: 0.100 + load[2]: 0.100 + load[3]: 42.130 + load[4]: 42.130 + ''' + self.do_run(src, re.sub('(^|\n)\s+', '\\1', expected)) + + def test_inet(self): + src = r''' + #include + #include + + int main() { + printf("*%x,%x,%x,%x,%x,%x*\n", htonl(0xa1b2c3d4), htonl(0xfe3572e0), htonl(0x07abcdf0), htons(0xabcd), ntohl(0x43211234), ntohs(0xbeaf)); + in_addr_t i = inet_addr("190.180.10.78"); + printf("%x\n", i); + return 0; + } + ''' + self.do_run(src, '*d4c3b2a1,e07235fe,f0cdab07,cdab,34122143,afbe*\n4e0ab4be\n') + + def test_inet2(self): + src = r''' + #include + #include + + int main() { + struct in_addr x, x2; + int *y = (int*)&x; + *y = 0x12345678; + printf("%s\n", inet_ntoa(x)); + int r = inet_aton(inet_ntoa(x), &x2); + printf("%s\n", inet_ntoa(x2)); + return 0; + } + ''' + self.do_run(src, '120.86.52.18\n120.86.52.18\n') + + def test_inet3(self): + src = r''' + #include + #include + #include + int main() { + char dst[64]; + struct in_addr x, x2; + int *y = (int*)&x; + *y = 0x12345678; + printf("%s\n", inet_ntop(AF_INET,&x,dst,sizeof dst)); + int r = inet_aton(inet_ntoa(x), &x2); + printf("%s\n", inet_ntop(AF_INET,&x2,dst,sizeof dst)); + return 0; + } + ''' + self.do_run(src, '120.86.52.18\n120.86.52.18\n') + + def test_inet4(self): + if Settings.USE_TYPED_ARRAYS != 2: return self.skip('requires ta2') + + src = r''' + #include + #include + #include + + void test(char *test_addr){ + char str[40]; + struct in6_addr addr; + unsigned char *p = (unsigned char*)&addr; + int ret; + ret = inet_pton(AF_INET6,test_addr,&addr); + if(ret == -1) return; + if(ret == 0) return; + if(inet_ntop(AF_INET6,&addr,str,sizeof(str)) == NULL ) return; + printf("%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x - %s\n", + p[0],p[1],p[2],p[3],p[4],p[5],p[6],p[7],p[8],p[9],p[10],p[11],p[12],p[13],p[14],p[15],str); + } + int main(){ + test("::"); + test("::1"); + test("::1.2.3.4"); + test("::17.18.19.20"); + test("::ffff:1.2.3.4"); + test("1::ffff"); + test("::255.255.255.255"); + test("0:ff00:1::"); + test("0:ff::"); + test("abcd::"); + test("ffff::a"); + test("ffff::a:b"); + test("ffff::a:b:c"); + test("ffff::a:b:c:d"); + test("ffff::a:b:c:d:e"); + test("::1:2:0:0:0"); + test("0:0:1:2:3::"); + test("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"); + test("1::255.255.255.255"); + + //below should fail and not produce results.. + test("1.2.3.4"); + test(""); + test("-"); + } + ''' + self.do_run(src, + "0000:0000:0000:0000:0000:0000:0000:0000 - ::\n" + "0000:0000:0000:0000:0000:0000:0000:0001 - ::1\n" + "0000:0000:0000:0000:0000:0000:0102:0304 - ::1.2.3.4\n" + "0000:0000:0000:0000:0000:0000:1112:1314 - ::17.18.19.20\n" + "0000:0000:0000:0000:0000:ffff:0102:0304 - ::ffff:1.2.3.4\n" + "0001:0000:0000:0000:0000:0000:0000:ffff - 1::ffff\n" + "0000:0000:0000:0000:0000:0000:ffff:ffff - ::255.255.255.255\n" + "0000:ff00:0001:0000:0000:0000:0000:0000 - 0:ff00:1::\n" + "0000:00ff:0000:0000:0000:0000:0000:0000 - 0:ff::\n" + "abcd:0000:0000:0000:0000:0000:0000:0000 - abcd::\n" + "ffff:0000:0000:0000:0000:0000:0000:000a - ffff::a\n" + "ffff:0000:0000:0000:0000:0000:000a:000b - ffff::a:b\n" + "ffff:0000:0000:0000:0000:000a:000b:000c - ffff::a:b:c\n" + "ffff:0000:0000:0000:000a:000b:000c:000d - ffff::a:b:c:d\n" + "ffff:0000:0000:000a:000b:000c:000d:000e - ffff::a:b:c:d:e\n" + "0000:0000:0000:0001:0002:0000:0000:0000 - ::1:2:0:0:0\n" + "0000:0000:0001:0002:0003:0000:0000:0000 - 0:0:1:2:3::\n" + "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff - ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff\n" + "0001:0000:0000:0000:0000:0000:ffff:ffff - 1::ffff:ffff\n" + ) + + def test_gethostbyname(self): + if Settings.USE_TYPED_ARRAYS != 2: return self.skip("assume t2 in gethostbyname") + + src = r''' + #include + #include + + void test(char *hostname) { + hostent *host = gethostbyname(hostname); + if (!host) { + printf("no such thing\n"); + return; + } + printf("%s : %d : %d\n", host->h_name, host->h_addrtype, host->h_length); + char **name = host->h_aliases; + while (*name) { + printf("- %s\n", *name); + name++; + } + name = host->h_addr_list; + while (name && *name) { + printf("* "); + for (int i = 0; i < host->h_length; i++) + printf("%d.", (*name)[i]); + printf("\n"); + name++; + } + } + + int main() { + test("www.cheezburger.com"); + test("fail.on.this.never.work"); // we will "work" on this - because we are just making aliases of names to ips + test("localhost"); + return 0; + } + ''' + self.do_run(src, '''www.cheezburger.com : 2 : 4 +* -84.29.1.0. +fail.on.this.never.work : 2 : 4 +* -84.29.2.0. +localhost : 2 : 4 +* -84.29.3.0. +''') + + def test_799(self): + src = open(path_from_root('tests', '799.cpp'), 'r').read() + self.do_run(src, '''Set PORT family: 0, port: 3979 +Get PORT family: 0 +PORT: 3979 +''') + + def test_ctype(self): + # The bit fiddling done by the macros using __ctype_b_loc requires this. + Settings.CORRECT_SIGNS = 1 + src = open(path_from_root('tests', 'ctype', 'src.c'), 'r').read() + expected = open(path_from_root('tests', 'ctype', 'output.txt'), 'r').read() + self.do_run(src, expected) + + def test_strcasecmp(self): + src = r''' + #include + #include + int sign(int x) { + if (x < 0) return -1; + if (x > 0) return 1; + return 0; + } + int main() { + printf("*\n"); + + printf("%d\n", sign(strcasecmp("hello", "hello"))); + printf("%d\n", sign(strcasecmp("hello1", "hello"))); + printf("%d\n", sign(strcasecmp("hello", "hello1"))); + printf("%d\n", sign(strcasecmp("hello1", "hello1"))); + printf("%d\n", sign(strcasecmp("iello", "hello"))); + printf("%d\n", sign(strcasecmp("hello", "iello"))); + printf("%d\n", sign(strcasecmp("A", "hello"))); + printf("%d\n", sign(strcasecmp("Z", "hello"))); + printf("%d\n", sign(strcasecmp("a", "hello"))); + printf("%d\n", sign(strcasecmp("z", "hello"))); + printf("%d\n", sign(strcasecmp("hello", "a"))); + printf("%d\n", sign(strcasecmp("hello", "z"))); + + printf("%d\n", sign(strcasecmp("Hello", "hello"))); + printf("%d\n", sign(strcasecmp("Hello1", "hello"))); + printf("%d\n", sign(strcasecmp("Hello", "hello1"))); + printf("%d\n", sign(strcasecmp("Hello1", "hello1"))); + printf("%d\n", sign(strcasecmp("Iello", "hello"))); + printf("%d\n", sign(strcasecmp("Hello", "iello"))); + printf("%d\n", sign(strcasecmp("A", "hello"))); + printf("%d\n", sign(strcasecmp("Z", "hello"))); + printf("%d\n", sign(strcasecmp("a", "hello"))); + printf("%d\n", sign(strcasecmp("z", "hello"))); + printf("%d\n", sign(strcasecmp("Hello", "a"))); + printf("%d\n", sign(strcasecmp("Hello", "z"))); + + printf("%d\n", sign(strcasecmp("hello", "Hello"))); + printf("%d\n", sign(strcasecmp("hello1", "Hello"))); + printf("%d\n", sign(strcasecmp("hello", "Hello1"))); + printf("%d\n", sign(strcasecmp("hello1", "Hello1"))); + printf("%d\n", sign(strcasecmp("iello", "Hello"))); + printf("%d\n", sign(strcasecmp("hello", "Iello"))); + printf("%d\n", sign(strcasecmp("A", "Hello"))); + printf("%d\n", sign(strcasecmp("Z", "Hello"))); + printf("%d\n", sign(strcasecmp("a", "Hello"))); + printf("%d\n", sign(strcasecmp("z", "Hello"))); + printf("%d\n", sign(strcasecmp("hello", "a"))); + printf("%d\n", sign(strcasecmp("hello", "z"))); + + printf("%d\n", sign(strcasecmp("Hello", "Hello"))); + printf("%d\n", sign(strcasecmp("Hello1", "Hello"))); + printf("%d\n", sign(strcasecmp("Hello", "Hello1"))); + printf("%d\n", sign(strcasecmp("Hello1", "Hello1"))); + printf("%d\n", sign(strcasecmp("Iello", "Hello"))); + printf("%d\n", sign(strcasecmp("Hello", "Iello"))); + printf("%d\n", sign(strcasecmp("A", "Hello"))); + printf("%d\n", sign(strcasecmp("Z", "Hello"))); + printf("%d\n", sign(strcasecmp("a", "Hello"))); + printf("%d\n", sign(strcasecmp("z", "Hello"))); + printf("%d\n", sign(strcasecmp("Hello", "a"))); + printf("%d\n", sign(strcasecmp("Hello", "z"))); + + printf("%d\n", sign(strncasecmp("hello", "hello", 3))); + printf("%d\n", sign(strncasecmp("hello1", "hello", 3))); + printf("%d\n", sign(strncasecmp("hello", "hello1", 3))); + printf("%d\n", sign(strncasecmp("hello1", "hello1", 3))); + printf("%d\n", sign(strncasecmp("iello", "hello", 3))); + printf("%d\n", sign(strncasecmp("hello", "iello", 3))); + printf("%d\n", sign(strncasecmp("A", "hello", 3))); + printf("%d\n", sign(strncasecmp("Z", "hello", 3))); + printf("%d\n", sign(strncasecmp("a", "hello", 3))); + printf("%d\n", sign(strncasecmp("z", "hello", 3))); + printf("%d\n", sign(strncasecmp("hello", "a", 3))); + printf("%d\n", sign(strncasecmp("hello", "z", 3))); + + printf("*\n"); + + return 0; + } + ''' + self.do_run(src, '''*\n0\n1\n-1\n0\n1\n-1\n-1\n1\n-1\n1\n1\n-1\n0\n1\n-1\n0\n1\n-1\n-1\n1\n-1\n1\n1\n-1\n0\n1\n-1\n0\n1\n-1\n-1\n1\n-1\n1\n1\n-1\n0\n1\n-1\n0\n1\n-1\n-1\n1\n-1\n1\n1\n-1\n0\n0\n0\n0\n1\n-1\n-1\n1\n-1\n1\n1\n-1\n*\n''') + + def test_atomic(self): + src = ''' + #include + int main() { + int x = 10; + int y = __sync_add_and_fetch(&x, 5); + printf("*%d,%d*\\n", x, y); + x = 10; + y = __sync_fetch_and_add(&x, 5); + printf("*%d,%d*\\n", x, y); + x = 10; + y = __sync_lock_test_and_set(&x, 6); + printf("*%d,%d*\\n", x, y); + x = 10; + y = __sync_bool_compare_and_swap(&x, 9, 7); + printf("*%d,%d*\\n", x, y); + y = __sync_bool_compare_and_swap(&x, 10, 7); + printf("*%d,%d*\\n", x, y); + return 0; + } + ''' + + self.do_run(src, '*15,15*\n*15,10*\n*6,10*\n*10,0*\n*7,1*') + + def test_phiundef(self): + src = r''' +#include +#include + +static int state; + +struct my_struct { +union { + struct { + unsigned char a; + unsigned char b; + } c; + unsigned int d; +} e; +unsigned int f; +}; + +int main(int argc, char **argv) { + struct my_struct r; + + state = 0; + + for (int i=0;i + + int main() + { + std::cout << "hello world" << std::endl << 77 << "." << std::endl; + return 0; + } + ''' + + # FIXME: should not have so many newlines in output here + self.do_run(src, 'hello world\n77.\n') + + def test_stdvec(self): + if self.emcc_args is None: return self.skip('requires emcc') + src = ''' + #include + #include + + struct S { + int a; + float b; + }; + + void foo(int a, float b) + { + printf("%d:%.2f\\n", a, b); + } + + int main ( int argc, char *argv[] ) + { + std::vector ar; + S s; + + s.a = 789; + s.b = 123.456f; + ar.push_back(s); + + s.a = 0; + s.b = 100.1f; + ar.push_back(s); + + foo(ar[0].a, ar[0].b); + foo(ar[1].a, ar[1].b); + } + ''' + + self.do_run(src, '789:123.46\n0:100.1') + + def test_reinterpreted_ptrs(self): + if self.emcc_args is None: return self.skip('needs emcc and libc') + + src = r''' +#include + +class Foo { +private: + float bar; +public: + int baz; + + Foo(): bar(0), baz(4711) {}; + + int getBar() const; +}; + +int Foo::getBar() const { + return this->bar; +}; + +const Foo *magic1 = reinterpret_cast(0xDEAD111F); +const Foo *magic2 = reinterpret_cast(0xDEAD888F); + +static void runTest() { + + const Foo *a = new Foo(); + const Foo *b = a; + + if (a->getBar() == 0) { + if (a->baz == 4712) + b = magic1; + else + b = magic2; + } + + printf("%s\n", (b == magic1 ? "magic1" : (b == magic2 ? "magic2" : "neither"))); +}; + +extern "C" { + int main(int argc, char **argv) { + runTest(); + } +} +''' + self.do_run(src, 'magic2') + + def test_jansson(self): + return self.skip('currently broken') + + if Settings.USE_TYPED_ARRAYS != 2: return self.skip('requires ta2') + if Settings.SAFE_HEAP: return self.skip('jansson is not safe-heap safe') + + src = ''' + #include + #include + #include + + int main() + { + const char* jsonString = "{\\"key\\": \\"value\\",\\"array\\": [\\"array_item1\\",\\"array_item2\\",\\"array_item3\\"],\\"dict\\":{\\"number\\": 3,\\"float\\": 2.2}}"; + + json_error_t error; + json_t *root = json_loadb(jsonString, strlen(jsonString), 0, &error); + + if(!root) { + printf("Node `root` is `null`."); + return 0; + } + + if(!json_is_object(root)) { + printf("Node `root` is no object."); + return 0; + } + + printf("%s\\n", json_string_value(json_object_get(root, "key"))); + + json_t *array = json_object_get(root, "array"); + if(!array) { + printf("Node `array` is `null`."); + return 0; + } + + if(!json_is_array(array)) { + printf("Node `array` is no array."); + return 0; + } + + for(size_t i=0; i1) + + def test_raytrace(self): + if self.emcc_args is None: return self.skip('requires emcc') + if Settings.USE_TYPED_ARRAYS == 2: return self.skip('Relies on double value rounding, extremely sensitive') + + src = open(path_from_root('tests', 'raytrace.cpp'), 'r').read().replace('double', 'float') + output = open(path_from_root('tests', 'raytrace.ppm'), 'r').read() + self.do_run(src, output, ['3', '16'])#, build_ll_hook=self.do_autodebug) + + def test_fasta(self): + if self.emcc_args is None: return self.skip('requires emcc') + results = [ (1,'''GG*ctt**tgagc*'''), (20,'''GGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTT*cttBtatcatatgctaKggNcataaaSatgtaaaDcDRtBggDtctttataattcBgtcg**tacgtgtagcctagtgtttgtgttgcgttatagtctatttgtggacacagtatggtcaaa**tgacgtcttttgatctgacggcgttaacaaagatactctg*'''), +(50,'''GGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGA*TCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACAT*cttBtatcatatgctaKggNcataaaSatgtaaaDcDRtBggDtctttataattcBgtcg**tactDtDagcctatttSVHtHttKtgtHMaSattgWaHKHttttagacatWatgtRgaaa**NtactMcSMtYtcMgRtacttctWBacgaa**agatactctgggcaacacacatacttctctcatgttgtttcttcggacctttcataacct**ttcctggcacatggttagctgcacatcacaggattgtaagggtctagtggttcagtgagc**ggaatatcattcgtcggtggtgttaatctatctcggtgtagcttataaatgcatccgtaa**gaatattatgtttatttgtcggtacgttcatggtagtggtgtcgccgatttagacgtaaa**ggcatgtatg*''') ] + for i, j in results: + src = open(path_from_root('tests', 'fasta.cpp'), 'r').read() + self.do_run(src, j, [str(i)], lambda x, err: x.replace('\n', '*'), no_build=i>1) + + def test_whets(self): + if not Settings.ASM_JS: return self.skip('mainly a test for asm validation here') + self.do_run(open(path_from_root('tests', 'whets.cpp')).read(), 'Single Precision C Whetstone Benchmark') + + def test_dlmalloc(self): + if self.emcc_args is None: self.emcc_args = [] # dlmalloc auto-inclusion is only done if we use emcc + + self.banned_js_engines = [NODE_JS] # slower, and fail on 64-bit + Settings.CORRECT_SIGNS = 2 + Settings.CORRECT_SIGNS_LINES = ['src.cpp:' + str(i+4) for i in [4816, 4191, 4246, 4199, 4205, 4235, 4227]] + Settings.TOTAL_MEMORY = 128*1024*1024 # needed with typed arrays + + src = open(path_from_root('system', 'lib', 'dlmalloc.c'), 'r').read() + '\n\n\n' + open(path_from_root('tests', 'dlmalloc_test.c'), 'r').read() + self.do_run(src, '*1,0*', ['200', '1']) + self.do_run(src, '*400,0*', ['400', '400'], no_build=True) + + # Linked version + src = open(path_from_root('tests', 'dlmalloc_test.c'), 'r').read() + self.do_run(src, '*1,0*', ['200', '1'], extra_emscripten_args=['-m']) + self.do_run(src, '*400,0*', ['400', '400'], extra_emscripten_args=['-m'], no_build=True) + + if self.emcc_args == []: # TODO: do this in other passes too, passing their opts into emcc + # emcc should build in dlmalloc automatically, and do all the sign correction etc. for it + + try_delete(os.path.join(self.get_dir(), 'src.cpp.o.js')) + output = Popen([PYTHON, EMCC, path_from_root('tests', 'dlmalloc_test.c'), '-s', 'TOTAL_MEMORY=' + str(128*1024*1024), + '-o', os.path.join(self.get_dir(), 'src.cpp.o.js')], stdout=PIPE, stderr=self.stderr_redirect).communicate() + + self.do_run('x', '*1,0*', ['200', '1'], no_build=True) + self.do_run('x', '*400,0*', ['400', '400'], no_build=True) + + # The same for new and all its variants + src = open(path_from_root('tests', 'new.cpp')).read() + for new, delete in [ + ('malloc(100)', 'free'), + ('new char[100]', 'delete[]'), + ('new Structy', 'delete'), + ('new int', 'delete'), + ('new Structy[10]', 'delete[]'), + ]: + self.do_run(src.replace('{{{ NEW }}}', new).replace('{{{ DELETE }}}', delete), '*1,0*') + + def test_dlmalloc_partial(self): + if self.emcc_args is None: return self.skip('only emcc will link in dlmalloc') + # present part of the symbols of dlmalloc, not all + src = open(path_from_root('tests', 'new.cpp')).read().replace('{{{ NEW }}}', 'new int').replace('{{{ DELETE }}}', 'delete') + ''' +void * +operator new(size_t size) +{ +printf("new %d!\\n", size); +return malloc(size); +} +''' + self.do_run(src, 'new 4!\n*1,0*') + + def test_dlmalloc_partial_2(self): + if self.emcc_args is None or 'SAFE_HEAP' in str(self.emcc_args) or 'CHECK_HEAP_ALIGN' in str(self.emcc_args): return self.skip('only emcc will link in dlmalloc, and we do unsafe stuff') + # present part of the symbols of dlmalloc, not all. malloc is harder to link than new which is weak. + src = r''' + #include + #include + void *malloc(size_t size) + { + return (void*)123; + } + int main() { + void *x = malloc(10); + printf("got %p\n", x); + free(x); + printf("freed the faker\n"); + return 1; + } +''' + self.do_run(src, 'got 0x7b\nfreed') + + def test_libcxx(self): + if self.emcc_args is None: return self.skip('requires emcc') + self.do_run(open(path_from_root('tests', 'hashtest.cpp')).read(), + 'june -> 30\nPrevious (in alphabetical order) is july\nNext (in alphabetical order) is march') + + self.do_run(''' + #include + #include + int main() { + std::set *fetchOriginatorNums = new std::set(); + fetchOriginatorNums->insert(171); + printf("hello world\\n"); + return 0; + } + ''', 'hello world'); + + def test_typeid(self): + self.do_run(r''' + #include + #include + #include + int main() { + printf("*\n"); + #define MAX 100 + int ptrs[MAX]; + int groups[MAX]; + memset(ptrs, 0, MAX*sizeof(int)); + memset(groups, 0, MAX*sizeof(int)); + int next_group = 1; + #define TEST(X) { \ + int ptr = (int)&typeid(X); \ + int group = 0; \ + int i; \ + for (i = 0; i < MAX; i++) { \ + if (!groups[i]) break; \ + if (ptrs[i] == ptr) { \ + group = groups[i]; \ + break; \ + } \ + } \ + if (!group) { \ + groups[i] = group = next_group++; \ + ptrs[i] = ptr; \ + } \ + printf("%s:%d\n", #X, group); \ + } + TEST(int); + TEST(unsigned int); + TEST(unsigned); + TEST(signed int); + TEST(long); + TEST(unsigned long); + TEST(signed long); + TEST(long long); + TEST(unsigned long long); + TEST(signed long long); + TEST(short); + TEST(unsigned short); + TEST(signed short); + TEST(char); + TEST(unsigned char); + TEST(signed char); + TEST(float); + TEST(double); + TEST(long double); + TEST(void); + TEST(void*); + printf("*\n"); + } + ''', '''* +int:1 +unsigned int:2 +unsigned:2 +signed int:1 +long:3 +unsigned long:4 +signed long:3 +long long:5 +unsigned long long:6 +signed long long:5 +short:7 +unsigned short:8 +signed short:7 +char:9 +unsigned char:10 +signed char:11 +float:12 +double:13 +long double:14 +void:15 +void*:16 +* +'''); + + def test_static_variable(self): + if self.emcc_args is None: Settings.SAFE_HEAP = 0 # LLVM mixes i64 and i8 in the guard check + src = ''' + #include + + struct DATA + { + int value; + + DATA() + { + value = 0; + } + }; + + DATA & GetData() + { + static DATA data; + + return data; + } + + int main() + { + GetData().value = 10; + printf( "value:%i", GetData().value ); + } + ''' + self.do_run(src, 'value:10') + + def test_fakestat(self): + src = r''' + #include + struct stat { int x, y; }; + int main() { + stat s; + s.x = 10; + s.y = 22; + printf("*%d,%d*\n", s.x, s.y); + } + ''' + self.do_run(src, '*10,22*') + + def test_mmap(self): + if self.emcc_args is None: return self.skip('requires emcc') + + Settings.TOTAL_MEMORY = 128*1024*1024 + + src = ''' + #include + #include + #include + + int main(int argc, char *argv[]) { + for (int i = 0; i < 10; i++) { + int* map = (int*)mmap(0, 5000, PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_ANON, -1, 0); + /* TODO: Should we align to 4k? + assert(((int)map) % 4096 == 0); // aligned + */ + assert(munmap(map, 5000) == 0); + } + + const int NUM_BYTES = 8 * 1024 * 1024; + const int NUM_INTS = NUM_BYTES / sizeof(int); + + int* map = (int*)mmap(0, NUM_BYTES, PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_ANON, -1, 0); + assert(map != MAP_FAILED); + + int i; + + for (i = 0; i < NUM_INTS; i++) { + map[i] = i; + } + + for (i = 0; i < NUM_INTS; i++) { + assert(map[i] == i); + } + + assert(munmap(map, NUM_BYTES) == 0); + + printf("hello,world"); + return 0; + } + ''' + self.do_run(src, 'hello,world') + self.do_run(src, 'hello,world', force_c=True) + + def test_mmap_file(self): + if self.emcc_args is None: return self.skip('requires emcc') + self.emcc_args += ['--embed-file', 'data.dat'] + + open(self.in_dir('data.dat'), 'w').write('data from the file ' + ('.' * 9000)) + + src = r''' + #include + #include + + int main() { + printf("*\n"); + FILE *f = fopen("data.dat", "r"); + char *m; + m = (char*)mmap(NULL, 9000, PROT_READ, MAP_PRIVATE, fileno(f), 0); + for (int i = 0; i < 20; i++) putchar(m[i]); + munmap(m, 9000); + printf("\n"); + m = (char*)mmap(NULL, 9000, PROT_READ, MAP_PRIVATE, fileno(f), 5); + for (int i = 0; i < 20; i++) putchar(m[i]); + munmap(m, 9000); + printf("\n*\n"); + return 0; + } + ''' + self.do_run(src, '*\ndata from the file .\nfrom the file ......\n*\n') + + def test_cubescript(self): + if self.emcc_args is None: return self.skip('requires emcc') + if self.run_name == 'o2': + self.emcc_args += ['--closure', '1'] # Use closure here for some additional coverage + + Building.COMPILER_TEST_OPTS = filter(lambda x: x != '-g', Building.COMPILER_TEST_OPTS) # remove -g, so we have one test without it by default + if self.emcc_args is None: Settings.SAFE_HEAP = 0 # Has some actual loads of unwritten-to places, in the C++ code... + + # Overflows happen in hash loop + Settings.CORRECT_OVERFLOWS = 1 + Settings.CHECK_OVERFLOWS = 0 + + if Settings.USE_TYPED_ARRAYS == 2: + Settings.CORRECT_SIGNS = 1 + + self.do_run(path_from_root('tests', 'cubescript'), '*\nTemp is 33\n9\n5\nhello, everyone\n*', main_file='command.cpp') + + assert 'asm2g' in test_modes + if self.run_name == 'asm2g': + results = {} + original = open('src.cpp.o.js').read() + results[Settings.ALIASING_FUNCTION_POINTERS] = len(original) + Settings.ALIASING_FUNCTION_POINTERS = 1 - Settings.ALIASING_FUNCTION_POINTERS + self.do_run(path_from_root('tests', 'cubescript'), '*\nTemp is 33\n9\n5\nhello, everyone\n*', main_file='command.cpp') + final = open('src.cpp.o.js').read() + results[Settings.ALIASING_FUNCTION_POINTERS] = len(final) + open('original.js', 'w').write(original) + print results + assert results[1] < 0.99*results[0] + assert ' & 3]()' in original, 'small function table exists' + assert ' & 3]()' not in final, 'small function table does not exist' + assert ' & 255]()' not in original, 'big function table does not exist' + assert ' & 255]()' in final, 'big function table exists' + + def test_gcc_unmangler(self): + Settings.NAMED_GLOBALS = 1 # test coverage for this + + Building.COMPILER_TEST_OPTS += ['-I' + path_from_root('third_party')] + + self.do_run(open(path_from_root('third_party', 'gcc_demangler.c')).read(), '*d_demangle(char const*, int, unsigned int*)*', args=['_ZL10d_demanglePKciPj']) + + #### Code snippet that is helpful to search for nonportable optimizations #### + #global LLVM_OPT_OPTS + #for opt in ['-aa-eval', '-adce', '-always-inline', '-argpromotion', '-basicaa', '-basiccg', '-block-placement', '-break-crit-edges', '-codegenprepare', '-constmerge', '-constprop', '-correlated-propagation', '-count-aa', '-dce', '-deadargelim', '-deadtypeelim', '-debug-aa', '-die', '-domfrontier', '-domtree', '-dse', '-extract-blocks', '-functionattrs', '-globaldce', '-globalopt', '-globalsmodref-aa', '-gvn', '-indvars', '-inline', '-insert-edge-profiling', '-insert-optimal-edge-profiling', '-instcombine', '-instcount', '-instnamer', '-internalize', '-intervals', '-ipconstprop', '-ipsccp', '-iv-users', '-jump-threading', '-lazy-value-info', '-lcssa', '-lda', '-libcall-aa', '-licm', '-lint', '-live-values', '-loop-deletion', '-loop-extract', '-loop-extract-single', '-loop-index-split', '-loop-reduce', '-loop-rotate', '-loop-unroll', '-loop-unswitch', '-loops', '-loopsimplify', '-loweratomic', '-lowerinvoke', '-lowersetjmp', '-lowerswitch', '-mem2reg', '-memcpyopt', '-memdep', '-mergefunc', '-mergereturn', '-module-debuginfo', '-no-aa', '-no-profile', '-partial-inliner', '-partialspecialization', '-pointertracking', '-postdomfrontier', '-postdomtree', '-preverify', '-prune-eh', '-reassociate', '-reg2mem', '-regions', '-scalar-evolution', '-scalarrepl', '-sccp', '-scev-aa', '-simplify-libcalls', '-simplify-libcalls-halfpowr', '-simplifycfg', '-sink', '-split-geps', '-sretpromotion', '-strip', '-strip-dead-debug-info', '-strip-dead-prototypes', '-strip-debug-declare', '-strip-nondebug', '-tailcallelim', '-tailduplicate', '-targetdata', '-tbaa']: + # LLVM_OPT_OPTS = [opt] + # try: + # self.do_run(path_from_root(['third_party']), '*d_demangle(char const*, int, unsigned int*)*', args=['_ZL10d_demanglePKciPj'], main_file='gcc_demangler.c') + # print opt, "ok" + # except: + # print opt, "FAIL" + + def test_lua(self): + if self.emcc_args is None: return self.skip('requires emcc') + if Settings.QUANTUM_SIZE == 1: return self.skip('TODO: make this work') + + self.do_run('', + 'hello lua world!\n17\n1\n2\n3\n4\n7', + args=['-e', '''print("hello lua world!");print(17);for x = 1,4 do print(x) end;print(10-3)'''], + libraries=self.get_library('lua', [os.path.join('src', 'lua'), os.path.join('src', 'liblua.a')], make=['make', 'generic'], configure=None), + includes=[path_from_root('tests', 'lua')], + output_nicerizer=lambda string, err: (string + err).replace('\n\n', '\n').replace('\n\n', '\n')) + + def get_freetype(self): + Settings.DEAD_FUNCTIONS += ['_inflateEnd', '_inflate', '_inflateReset', '_inflateInit2_'] + + return self.get_library('freetype', + os.path.join('objs', '.libs', 'libfreetype.a')) + + def test_freetype(self): + if self.emcc_args is None: return self.skip('requires emcc') + if Settings.QUANTUM_SIZE == 1: return self.skip('TODO: Figure out and try to fix') + if Settings.ASM_JS and '-O2' not in self.emcc_args: return self.skip('mozilla bug 863867') + + if Settings.CORRECT_SIGNS == 0: Settings.CORRECT_SIGNS = 1 # Not sure why, but needed + + post = ''' +def process(filename): + import tools.shared as shared + # Embed the font into the document + src = open(filename, 'r').read().replace( + '// {{PRE_RUN_ADDITIONS}}', + "FS.createDataFile('/', 'font.ttf', %s, true, false);" % str( + map(ord, open(shared.path_from_root('tests', 'freetype', 'LiberationSansBold.ttf'), 'rb').read()) + ) + ) + open(filename, 'w').write(src) +''' + + # Not needed for js, but useful for debugging + shutil.copyfile(path_from_root('tests', 'freetype', 'LiberationSansBold.ttf'), os.path.join(self.get_dir(), 'font.ttf')) + + # Main + self.do_run(open(path_from_root('tests', 'freetype', 'main.c'), 'r').read(), + open(path_from_root('tests', 'freetype', 'ref.txt'), 'r').read(), + ['font.ttf', 'test!', '150', '120', '25'], + libraries=self.get_freetype(), + includes=[path_from_root('tests', 'freetype', 'include')], + post_build=post) + #build_ll_hook=self.do_autodebug) + + # github issue 324 + print '[issue 324]' + self.do_run(open(path_from_root('tests', 'freetype', 'main_2.c'), 'r').read(), + open(path_from_root('tests', 'freetype', 'ref_2.txt'), 'r').read(), + ['font.ttf', 'w', '32', '32', '25'], + libraries=self.get_freetype(), + includes=[path_from_root('tests', 'freetype', 'include')], + post_build=post) + + print '[issue 324 case 2]' + self.do_run(open(path_from_root('tests', 'freetype', 'main_3.c'), 'r').read(), + open(path_from_root('tests', 'freetype', 'ref_3.txt'), 'r').read(), + ['font.ttf', 'W', '32', '32', '0'], + libraries=self.get_freetype(), + includes=[path_from_root('tests', 'freetype', 'include')], + post_build=post) + + print '[issue 324 case 3]' + self.do_run('', + open(path_from_root('tests', 'freetype', 'ref_4.txt'), 'r').read(), + ['font.ttf', 'ea', '40', '32', '0'], + no_build=True) + + def test_sqlite(self): + # gcc -O3 -I/home/alon/Dev/emscripten/tests/sqlite -ldl src.c + if self.emcc_args is None: return self.skip('Very slow without ta2, and we would also need to include dlmalloc manually without emcc') + if Settings.QUANTUM_SIZE == 1: return self.skip('TODO FIXME') + self.banned_js_engines = [NODE_JS] # OOM in older node + + Settings.CORRECT_SIGNS = 1 + Settings.CORRECT_OVERFLOWS = 0 + Settings.CORRECT_ROUNDINGS = 0 + if self.emcc_args is None: Settings.SAFE_HEAP = 0 # uses time.h to set random bytes, other stuff + Settings.DISABLE_EXCEPTION_CATCHING = 1 + Settings.FAST_MEMORY = 4*1024*1024 + Settings.EXPORTED_FUNCTIONS += ['_sqlite3_open', '_sqlite3_close', '_sqlite3_exec', '_sqlite3_free', '_callback']; + if Settings.ASM_JS == 1 and '-g' in self.emcc_args: + print "disabling inlining" # without registerize (which -g disables), we generate huge amounts of code + Settings.INLINING_LIMIT = 50 + + self.do_run(r''' + #define SQLITE_DISABLE_LFS + #define LONGDOUBLE_TYPE double + #define SQLITE_INT64_TYPE long long int + #define SQLITE_THREADSAFE 0 + ''' + open(path_from_root('tests', 'sqlite', 'sqlite3.c'), 'r').read() + + open(path_from_root('tests', 'sqlite', 'benchmark.c'), 'r').read(), + open(path_from_root('tests', 'sqlite', 'benchmark.txt'), 'r').read(), + includes=[path_from_root('tests', 'sqlite')], + force_c=True) + + def test_zlib(self): + if not Settings.USE_TYPED_ARRAYS == 2: return self.skip('works in general, but cached build will be optimized and fail, so disable this') + + if Settings.ASM_JS: + self.banned_js_engines = [NODE_JS] # TODO investigate + + if self.emcc_args is not None and '-O2' in self.emcc_args and 'ASM_JS=0' not in self.emcc_args: # without asm, closure minifies Math.imul badly + self.emcc_args += ['--closure', '1'] # Use closure here for some additional coverage + + Settings.CORRECT_SIGNS = 1 + + self.do_run(open(path_from_root('tests', 'zlib', 'example.c'), 'r').read(), + open(path_from_root('tests', 'zlib', 'ref.txt'), 'r').read(), + libraries=self.get_library('zlib', os.path.join('libz.a'), make_args=['libz.a']), + includes=[path_from_root('tests', 'zlib')], + force_c=True) + + def test_the_bullet(self): # Called thus so it runs late in the alphabetical cycle... it is long + if self.emcc_args is None: return self.skip('requires emcc') + if Building.LLVM_OPTS and self.emcc_args is None: Settings.SAFE_HEAP = 0 # Optimizations make it so we do not have debug info on the line we need to ignore + + Settings.DEAD_FUNCTIONS = ['__ZSt9terminatev'] + + # Note: this is also a good test of per-file and per-line changes (since we have multiple files, and correct specific lines) + if Settings.SAFE_HEAP: + # Ignore bitfield warnings + Settings.SAFE_HEAP = 3 + Settings.SAFE_HEAP_LINES = ['btVoronoiSimplexSolver.h:40', 'btVoronoiSimplexSolver.h:41', + 'btVoronoiSimplexSolver.h:42', 'btVoronoiSimplexSolver.h:43'] + + def test(): + self.do_run(open(path_from_root('tests', 'bullet', 'Demos', 'HelloWorld', 'HelloWorld.cpp'), 'r').read(), + [open(path_from_root('tests', 'bullet', 'output.txt'), 'r').read(), # different roundings + open(path_from_root('tests', 'bullet', 'output2.txt'), 'r').read(), + open(path_from_root('tests', 'bullet', 'output3.txt'), 'r').read()], + libraries=self.get_library('bullet', [os.path.join('src', '.libs', 'libBulletDynamics.a'), + os.path.join('src', '.libs', 'libBulletCollision.a'), + os.path.join('src', '.libs', 'libLinearMath.a')], + configure_args=['--disable-demos','--disable-dependency-tracking']), + includes=[path_from_root('tests', 'bullet', 'src')]) + test() + + assert 'asm2g' in test_modes + if self.run_name == 'asm2g': + # Test forced alignment + print >> sys.stderr, 'testing FORCE_ALIGNED_MEMORY' + old = open('src.cpp.o.js').read() + Settings.FORCE_ALIGNED_MEMORY = 1 + test() + new = open('src.cpp.o.js').read() + print len(old), len(new), old.count('tempBigInt'), new.count('tempBigInt') + assert len(old) > len(new) + assert old.count('tempBigInt') > new.count('tempBigInt') + + def test_poppler(self): + if self.emcc_args is None: return self.skip('very slow, we only do this in emcc runs') + + Settings.CORRECT_OVERFLOWS = 1 + Settings.CORRECT_SIGNS = 1 + + Building.COMPILER_TEST_OPTS += [ + '-I' + path_from_root('tests', 'freetype', 'include'), + '-I' + path_from_root('tests', 'poppler', 'include'), + ] + + Settings.INVOKE_RUN = 0 # We append code that does run() ourselves + + # See post(), below + input_file = open(os.path.join(self.get_dir(), 'paper.pdf.js'), 'w') + input_file.write(str(map(ord, open(path_from_root('tests', 'poppler', 'paper.pdf'), 'rb').read()))) + input_file.close() + + post = ''' +def process(filename): + # To avoid loading this large file to memory and altering it, we simply append to the end + src = open(filename, 'a') + src.write( + \'\'\' + FS.createDataFile('/', 'paper.pdf', eval(Module.read('paper.pdf.js')), true, false); + Module.callMain(Module.arguments); + Module.print("Data: " + JSON.stringify(FS.root.contents['filename-1.ppm'].contents.map(function(x) { return unSign(x, 8) }))); + \'\'\' + ) + src.close() +''' + + #fontconfig = self.get_library('fontconfig', [os.path.join('src', '.libs', 'libfontconfig.a')]) # Used in file, but not needed, mostly + + freetype = self.get_freetype() + + poppler = self.get_library('poppler', + [os.path.join('utils', 'pdftoppm.o'), + os.path.join('utils', 'parseargs.o'), + os.path.join('poppler', '.libs', 'libpoppler.a')], + env_init={ 'FONTCONFIG_CFLAGS': ' ', 'FONTCONFIG_LIBS': ' ' }, + configure_args=['--disable-libjpeg', '--disable-libpng', '--disable-poppler-qt', '--disable-poppler-qt4', '--disable-cms', '--disable-cairo-output', '--disable-abiword-output', '--enable-shared=no']) + + # Combine libraries + + combined = os.path.join(self.get_dir(), 'poppler-combined.bc') + Building.link(poppler + freetype, combined) + + self.do_ll_run(combined, + map(ord, open(path_from_root('tests', 'poppler', 'ref.ppm'), 'r').read()).__str__().replace(' ', ''), + args='-scale-to 512 paper.pdf filename'.split(' '), + post_build=post) + #, build_ll_hook=self.do_autodebug) + + def test_openjpeg(self): + if self.emcc_args is None: return self.skip('needs libc for getopt') + + Building.COMPILER_TEST_OPTS = filter(lambda x: x != '-g', Building.COMPILER_TEST_OPTS) # remove -g, so we have one test without it by default + + if Settings.USE_TYPED_ARRAYS == 2: + Settings.CORRECT_SIGNS = 1 + else: + Settings.CORRECT_SIGNS = 2 + Settings.CORRECT_SIGNS_LINES = ["mqc.c:566", "mqc.c:317"] + + post = ''' +def process(filename): + import tools.shared as shared + original_j2k = shared.path_from_root('tests', 'openjpeg', 'syntensity_lobby_s.j2k') + src = open(filename, 'r').read().replace( + '// {{PRE_RUN_ADDITIONS}}', + "FS.createDataFile('/', 'image.j2k', %s, true, false);" % shared.line_splitter(str( + map(ord, open(original_j2k, 'rb').read()) + )) + ).replace( + '// {{POST_RUN_ADDITIONS}}', + "Module.print('Data: ' + JSON.stringify(FS.analyzePath('image.raw').object.contents));" + ) + open(filename, 'w').write(src) +''' + + shutil.copy(path_from_root('tests', 'openjpeg', 'opj_config.h'), self.get_dir()) + + lib = self.get_library('openjpeg', + [os.path.sep.join('codec/CMakeFiles/j2k_to_image.dir/index.c.o'.split('/')), + os.path.sep.join('codec/CMakeFiles/j2k_to_image.dir/convert.c.o'.split('/')), + os.path.sep.join('codec/CMakeFiles/j2k_to_image.dir/__/common/color.c.o'.split('/')), + os.path.join('bin', 'libopenjpeg.so.1.4.0')], + configure=['cmake', '.'], + #configure_args=['--enable-tiff=no', '--enable-jp3d=no', '--enable-png=no'], + make_args=[]) # no -j 2, since parallel builds can fail + + # We use doubles in JS, so we get slightly different values than native code. So we + # check our output by comparing the average pixel difference + def image_compare(output, err): + # Get the image generated by JS, from the JSON.stringify'd array + m = re.search('\[[\d, -]*\]', output) + try: + js_data = eval(m.group(0)) + except AttributeError: + print 'Failed to find proper image output in: ' + output + raise + + js_data = map(lambda x: x if x >= 0 else 256+x, js_data) # Our output may be signed, so unsign it + + # Get the correct output + true_data = open(path_from_root('tests', 'openjpeg', 'syntensity_lobby_s.raw'), 'rb').read() + + # Compare them + assert(len(js_data) == len(true_data)) + num = len(js_data) + diff_total = js_total = true_total = 0 + for i in range(num): + js_total += js_data[i] + true_total += ord(true_data[i]) + diff_total += abs(js_data[i] - ord(true_data[i])) + js_mean = js_total/float(num) + true_mean = true_total/float(num) + diff_mean = diff_total/float(num) + + image_mean = 83.265 + #print '[image stats:', js_mean, image_mean, true_mean, diff_mean, num, ']' + assert abs(js_mean - image_mean) < 0.01 + assert abs(true_mean - image_mean) < 0.01 + assert diff_mean < 0.01 + + return output + + self.emcc_args += ['--minify', '0'] # to compare the versions + + def do_test(): + self.do_run(open(path_from_root('tests', 'openjpeg', 'codec', 'j2k_to_image.c'), 'r').read(), + 'Successfully generated', # The real test for valid output is in image_compare + '-i image.j2k -o image.raw'.split(' '), + libraries=lib, + includes=[path_from_root('tests', 'openjpeg', 'libopenjpeg'), + path_from_root('tests', 'openjpeg', 'codec'), + path_from_root('tests', 'openjpeg', 'common'), + os.path.join(self.get_build_dir(), 'openjpeg')], + force_c=True, + post_build=post, + output_nicerizer=image_compare)#, build_ll_hook=self.do_autodebug) + + do_test() + + # some test coverage for EMCC_DEBUG 1 and 2 + if self.emcc_args and '-O2' in self.emcc_args and 'EMCC_DEBUG' not in os.environ: + shutil.copyfile('src.c.o.js', 'release.js') + try: + os.environ['EMCC_DEBUG'] = '1' + print '2' + do_test() + shutil.copyfile('src.c.o.js', 'debug1.js') + os.environ['EMCC_DEBUG'] = '2' + print '3' + do_test() + shutil.copyfile('src.c.o.js', 'debug2.js') + finally: + del os.environ['EMCC_DEBUG'] + for debug in [1,2]: + def clean(text): + return text.replace('\n\n', '\n').replace('\n\n', '\n').replace('\n\n', '\n').replace('\n\n', '\n').replace('\n\n', '\n').replace('{\n}', '{}') + self.assertIdentical(clean(open('release.js').read()), clean(open('debug%d.js' % debug).read())) # EMCC_DEBUG=1 mode must not generate different code! + print >> sys.stderr, 'debug check %d passed too' % debug + + try: + os.environ['EMCC_FORCE_STDLIBS'] = '1' + print 'EMCC_FORCE_STDLIBS' + do_test() + finally: + del os.environ['EMCC_FORCE_STDLIBS'] + print >> sys.stderr, 'EMCC_FORCE_STDLIBS ok' + + try_delete(CANONICAL_TEMP_DIR) + else: + print >> sys.stderr, 'not doing debug check' + + def test_python(self): + if self.emcc_args is None: return self.skip('requires emcc') + if Settings.QUANTUM_SIZE == 1: return self.skip('TODO: make this work') + if not self.is_le32(): return self.skip('fails on non-le32') # FIXME + + #Settings.EXPORTED_FUNCTIONS += ['_PyRun_SimpleStringFlags'] # for the demo + + if self.is_le32(): + bitcode = path_from_root('tests', 'python', 'python.le32.bc') + else: + bitcode = path_from_root('tests', 'python', 'python.small.bc') + + self.do_ll_run(bitcode, + 'hello python world!\n[0, 2, 4, 6]\n5\n22\n5.470000', + args=['-S', '-c' '''print "hello python world!"; print [x*2 for x in range(4)]; t=2; print 10-3-t; print (lambda x: x*2)(11); print '%f' % 5.47''']) + + def test_lifetime(self): + if self.emcc_args is None: return self.skip('test relies on emcc opts') + + self.do_ll_run(path_from_root('tests', 'lifetime.ll'), 'hello, world!\n') + if '-O1' in self.emcc_args or '-O2' in self.emcc_args: + assert 'a18' not in open(os.path.join(self.get_dir(), 'src.cpp.o.js')).read(), 'lifetime stuff and their vars must be culled' + + # Test cases in separate files. Note that these files may contain invalid .ll! + # They are only valid enough for us to read for test purposes, not for llvm-as + # to process. + def test_cases(self): + if Building.LLVM_OPTS: return self.skip("Our code is not exactly 'normal' llvm assembly") + + try: + os.environ['EMCC_LEAVE_INPUTS_RAW'] = '1' + Settings.CHECK_OVERFLOWS = 0 + + for name in glob.glob(path_from_root('tests', 'cases', '*.ll')): + shortname = name.replace('.ll', '') + if '' not in shortname: continue + if '_ta2' in shortname and not Settings.USE_TYPED_ARRAYS == 2: + print self.skip('case "%s" only relevant for ta2' % shortname) + continue + if '_noasm' in shortname and Settings.ASM_JS: + print self.skip('case "%s" not relevant for asm.js' % shortname) + continue + print >> sys.stderr, "Testing case '%s'..." % shortname + output_file = path_from_root('tests', 'cases', shortname + '.txt') + if Settings.QUANTUM_SIZE == 1: + q1_output_file = path_from_root('tests', 'cases', shortname + '_q1.txt') + if os.path.exists(q1_output_file): + output_file = q1_output_file + if os.path.exists(output_file): + output = open(output_file, 'r').read() + else: + output = 'hello, world!' + if output.rstrip() != 'skip': + self.do_ll_run(path_from_root('tests', 'cases', name), output) + # Optional source checking, a python script that gets a global generated with the source + src_checker = path_from_root('tests', 'cases', shortname + '.py') + if os.path.exists(src_checker): + generated = open('src.cpp.o.js').read() + exec(open(src_checker).read()) + + finally: + del os.environ['EMCC_LEAVE_INPUTS_RAW'] + + def test_fuzz(self): + if Settings.USE_TYPED_ARRAYS != 2: return self.skip('needs ta2') + + Building.COMPILER_TEST_OPTS += ['-I' + path_from_root('tests', 'fuzz')] + + def run_all(x): + print x + for name in glob.glob(path_from_root('tests', 'fuzz', '*.c')): + print name + self.do_run(open(path_from_root('tests', 'fuzz', name)).read(), + open(path_from_root('tests', 'fuzz', name + '.txt')).read(), force_c=True) + + run_all('normal') + + self.emcc_args += ['--llvm-lto', '1'] + + run_all('lto') + + # Autodebug the code + def do_autodebug(self, filename): + output = Popen([PYTHON, AUTODEBUGGER, filename+'.o.ll', filename+'.o.ll.ll'], stdout=PIPE, stderr=self.stderr_redirect).communicate()[0] + assert 'Success.' in output, output + self.prep_ll_run(filename, filename+'.o.ll.ll', force_recompile=True) # rebuild .bc # TODO: use code in do_autodebug_post for this + + # Autodebug the code, after LLVM opts. Will only work once! + def do_autodebug_post(self, filename): + if not hasattr(self, 'post'): + print 'Asking for post re-call' + self.post = True + return True + print 'Autodebugging during post time' + delattr(self, 'post') + output = Popen([PYTHON, AUTODEBUGGER, filename+'.o.ll', filename+'.o.ll.ll'], stdout=PIPE, stderr=self.stderr_redirect).communicate()[0] + assert 'Success.' in output, output + shutil.copyfile(filename + '.o.ll.ll', filename + '.o.ll') + Building.llvm_as(filename) + Building.llvm_dis(filename) + + def test_autodebug(self): + if Building.LLVM_OPTS: return self.skip('LLVM opts mess us up') + Building.COMPILER_TEST_OPTS += ['--llvm-opts', '0'] + + # Run a test that should work, generating some code + self.test_structs() + + filename = os.path.join(self.get_dir(), 'src.cpp') + self.do_autodebug(filename) + + # Compare to each other, and to expected output + self.do_ll_run(path_from_root('tests', filename+'.o.ll.ll'), '''AD:-1,1''') + assert open('stdout').read().startswith('AD:-1'), 'We must note when we enter functions' + + # Test using build_ll_hook + src = ''' + #include + + char cache[256], *next = cache; + + int main() + { + cache[10] = 25; + next[20] = 51; + int x = cache[10]; + double y = 11.52; + printf("*%d,%d,%.2f*\\n", x, cache[20], y); + return 0; + } + ''' + self.do_run(src, '''AD:-1,1''', build_ll_hook=self.do_autodebug) + + def test_corruption(self): + if Settings.ASM_JS: return self.skip('cannot use corruption checks in asm') + if Settings.USE_TYPED_ARRAYS != 2: return self.skip('needs ta2 for actual test') + + Settings.CORRUPTION_CHECK = 1 + + src = r''' + #include + #include + #include + int main(int argc, char **argv) { + int size = 1024*argc; + char *buffer = (char*)malloc(size); + #if CORRUPT + memset(buffer, argc, size+15); + #else + memset(buffer, argc, size); + #endif + for (int x = 0; x < size; x += argc*3) buffer[x] = x/3; + int ret = 0; + for (int x = 0; x < size; x++) ret += buffer[x]; + free(buffer); + printf("All ok, %d\n", ret); + } + ''' + + for corrupt in [1]: + self.do_run(src.replace('CORRUPT', str(corrupt)), 'Heap corruption detected!' if corrupt else 'All ok, 4209') + + def test_corruption_2(self): + if Settings.ASM_JS: return self.skip('cannot use corruption checks in asm') + if Settings.USE_TYPED_ARRAYS != 2: return self.skip('needs ta2 for actual test') + + Settings.SAFE_HEAP = 1 + Settings.CORRUPTION_CHECK = 1 + + # test for free(0), malloc(0), etc. + src = r''' + #include + #include + #include + #include + + void bye() { + printf("all ok\n"); + } + + int main() { + atexit(bye); + + std::string testPath = "/Script/WA-KA.txt"; + std::fstream str(testPath.c_str(), std::ios::in | std::ios::binary); + + if (str.is_open()) + { + std::cout << "open!" << std::endl; + } else { + std::cout << "missing!" << std::endl; + } + + return 1; + } + ''' + self.do_run(src, 'missing!\nall ok\n') + + def test_corruption_3(self): + if Settings.ASM_JS: return self.skip('cannot use corruption checks in asm') + if Settings.USE_TYPED_ARRAYS != 2: return self.skip('needs ta2 for actual test') + + Settings.CORRUPTION_CHECK = 1 + + # realloc + src = r''' + #include + #include + #include + + void bye() { + printf("all ok\n"); + } + + int main(int argc, char **argv) { + atexit(bye); + + char *buffer = (char*)malloc(100); + for (int i = 0; i < 100; i++) buffer[i] = (i*i)%256; + buffer = (char*)realloc(buffer, argc + 50); + for (int i = 0; i < argc + 50; i++) { + //printf("%d : %d : %d : %d\n", i, (int)(buffer + i), buffer[i], (char)((i*i)%256)); + assert(buffer[i] == (char)((i*i)%256)); + } + return 1; + } + ''' + self.do_run(src, 'all ok\n') + + ### Integration tests + + def test_ccall(self): + 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 + + src = r''' + #include + #include + + extern "C" { + int get_int() { return 5; } + float get_float() { return 3.14; } + char * get_string() { return "hello world"; } + void print_int(int x) { printf("%d\n", x); } + void print_float(float x) { printf("%.2f\n", x); } + void print_string(char *x) { printf("%s\n", x); } + int multi(int x, float y, int z, char *str) { if (x) puts(str); return (x+y)*z; } + int * pointer(int *in) { printf("%d\n", *in); static int ret = 21; return &ret; } + } + + int main(int argc, char **argv) { + return 0; + } + ''' + + post = ''' +def process(filename): + src = \'\'\' + var Module = { 'noInitialRun': true }; + \'\'\' + open(filename, 'r').read() + \'\'\' + Module.addOnExit(function () { + Module.print('*'); + var ret; + ret = Module['ccall']('get_int', 'number'); Module.print([typeof ret, ret]); + ret = ccall('get_float', 'number'); Module.print([typeof ret, ret.toFixed(2)]); + ret = ccall('get_string', 'string'); Module.print([typeof ret, ret]); + ret = ccall('print_int', null, ['number'], [12]); Module.print(typeof ret); + ret = ccall('print_float', null, ['number'], [14.56]); Module.print(typeof ret); + ret = ccall('print_string', null, ['string'], ["cheez"]); Module.print(typeof ret); + ret = ccall('print_string', null, ['array'], [[97, 114, 114, 45, 97, 121, 0]]); Module.print(typeof ret); + ret = ccall('multi', 'number', ['number', 'number', 'number', 'string'], [2, 1.4, 3, 'more']); Module.print([typeof ret, ret]); + var p = ccall('malloc', 'pointer', ['number'], [4]); + setValue(p, 650, 'i32'); + ret = ccall('pointer', 'pointer', ['pointer'], [p]); Module.print([typeof ret, getValue(ret, 'i32')]); + Module.print('*'); + // part 2: cwrap + var multi = Module['cwrap']('multi', 'number', ['number', 'number', 'number', 'string']); + Module.print(multi(2, 1.4, 3, 'atr')); + Module.print(multi(8, 5.4, 4, 'bret')); + Module.print('*'); + // part 3: avoid stack explosion + for (var i = 0; i < TOTAL_STACK/60; i++) { + ccall('multi', 'number', ['number', 'number', 'number', 'string'], [0, 0, 0, '123456789012345678901234567890123456789012345678901234567890']); + } + Module.print('stack is ok.'); + }); + Module.callMain(); + \'\'\' + open(filename, 'w').write(src) +''' + + Settings.EXPORTED_FUNCTIONS += ['_get_int', '_get_float', '_get_string', '_print_int', '_print_float', '_print_string', '_multi', '_pointer', '_malloc'] + + self.do_run(src, '*\nnumber,5\nnumber,3.14\nstring,hello world\n12\nundefined\n14.56\nundefined\ncheez\nundefined\narr-ay\nundefined\nmore\nnumber,10\n650\nnumber,21\n*\natr\n10\nbret\n53\n*\nstack is ok.\n', post_build=post) + + def test_pgo(self): + if Settings.ASM_JS: return self.skip('PGO does not work in asm mode') + + def run_all(name, src): + print name + def test(expected, args=[], no_build=False): + self.do_run(src, expected, args=args, no_build=no_build) + return open(self.in_dir('src.cpp.o.js')).read() + + # Sanity check that it works and the dead function is emitted + js = test('*9*') + assert 'function _unused(' in js + + # Run with PGO, see that unused is true to its name + Settings.PGO = 1 + test("*9*\n-s DEAD_FUNCTIONS='[\"_unused\"]'") + Settings.PGO = 0 + + # Kill off the dead function, still works and it is not emitted + Settings.DEAD_FUNCTIONS = ['_unused'] + js = test('*9*') + assert 'function _unused($' not in js # no compiled code + assert 'function _unused(' in js # lib-generated stub + Settings.DEAD_FUNCTIONS = [] + + # Run the same code with argc that uses the dead function, see abort + test(('missing function: unused'), args=['a', 'b'], no_build=True) + + # Normal stuff + run_all('normal', r''' + #include + extern "C" { + int used(int x) { + if (x == 0) return -1; + return used(x/3) + used(x/17) + x%5; + } + int unused(int x) { + if (x == 0) return -1; + return unused(x/4) + unused(x/23) + x%7; + } + } + int main(int argc, char **argv) { + printf("*%d*\n", argc == 3 ? unused(argv[0][0] + 1024) : used(argc + 1555)); + return 0; + } + ''') + + # Call by function pointer + run_all('function pointers', r''' + #include + extern "C" { + int used(int x) { + if (x == 0) return -1; + return used(x/3) + used(x/17) + x%5; + } + int unused(int x) { + if (x == 0) return -1; + return unused(x/4) + unused(x/23) + x%7; + } + } + typedef int (*ii)(int); + int main(int argc, char **argv) { + ii pointers[256]; + for (int i = 0; i < 256; i++) { + pointers[i] = (i == 3) ? unused : used; + } + printf("*%d*\n", pointers[argc](argc + 1555)); + return 0; + } + ''') + + def test_asm_pgo(self): + if not Settings.ASM_JS: return self.skip('this is a test for PGO for asm (NB: not *in* asm)') + + src = open(path_from_root('tests', 'hello_libcxx.cpp')).read() + output = 'hello, world!' + + self.do_run(src, output) + shutil.move(self.in_dir('src.cpp.o.js'), self.in_dir('normal.js')) + + Settings.ASM_JS = 0 + Settings.PGO = 1 + self.do_run(src, output) + Settings.ASM_JS = 1 + Settings.PGO = 0 + + shutil.move(self.in_dir('src.cpp.o.js'), self.in_dir('pgo.js')) + pgo_output = run_js(self.in_dir('pgo.js')).split('\n')[1] + open('pgo_data.rsp', 'w').write(pgo_output) + + # with response file + + self.emcc_args += ['@pgo_data.rsp'] + self.do_run(src, output) + self.emcc_args.pop() + shutil.move(self.in_dir('src.cpp.o.js'), self.in_dir('pgoed.js')) + + before = len(open('normal.js').read()) + after = len(open('pgoed.js').read()) + assert after < 0.90 * before, [before, after] # expect a size reduction + + # with response in settings element itself + + open('dead_funcs', 'w').write(pgo_output[pgo_output.find('['):-1]) + self.emcc_args += ['-s', 'DEAD_FUNCTIONS=@' + self.in_dir('dead_funcs')] + self.do_run(src, output) + self.emcc_args.pop() + self.emcc_args.pop() + shutil.move(self.in_dir('src.cpp.o.js'), self.in_dir('pgoed2.js')) + assert open('pgoed.js').read() == open('pgoed2.js').read() + + # with relative response in settings element itself + + open('dead_funcs', 'w').write(pgo_output[pgo_output.find('['):-1]) + self.emcc_args += ['-s', 'DEAD_FUNCTIONS=@dead_funcs'] + self.do_run(src, output) + self.emcc_args.pop() + self.emcc_args.pop() + shutil.move(self.in_dir('src.cpp.o.js'), self.in_dir('pgoed2.js')) + assert open('pgoed.js').read() == open('pgoed2.js').read() + + def test_exported_response(self): + if self.emcc_args is None: return self.skip('requires emcc') + + src = r''' + #include + #include + + extern "C" { + int other_function() { return 5; } + } + + int main() { + printf("waka!\n"); + return 0; + } + ''' + open('exps', 'w').write('["_main","_other_function"]') + + self.emcc_args += ['-s', 'EXPORTED_FUNCTIONS=@exps'] + self.do_run(src, '''waka!''') + assert 'other_function' in open('src.cpp.o.js').read() + + def test_add_function(self): + if self.emcc_args is None: return self.skip('requires emcc') + + Settings.INVOKE_RUN = 0 + Settings.RESERVED_FUNCTION_POINTERS = 1 + + src = r''' + #include + #include + + int main(int argc, char **argv) { + int fp = atoi(argv[1]); + printf("fp: %d\n", fp); + void (*f)(int) = reinterpret_cast(fp); + f(7); + return 0; + } + ''' + + open(os.path.join(self.get_dir(), 'post.js'), 'w').write(''' + var newFuncPtr = Runtime.addFunction(function(num) { + Module.print('Hello ' + num + ' from JS!'); + }); + Module.callMain([newFuncPtr.toString()]); + ''') + + self.emcc_args += ['--post-js', 'post.js'] + self.do_run(src, '''Hello 7 from JS!''') + + if Settings.ASM_JS: + Settings.RESERVED_FUNCTION_POINTERS = 0 + self.do_run(src, '''Finished up all reserved function pointers. Use a higher value for RESERVED_FUNCTION_POINTERS.''') + generated = open('src.cpp.o.js').read() + assert 'jsCall' not in generated + Settings.RESERVED_FUNCTION_POINTERS = 1 + + Settings.ALIASING_FUNCTION_POINTERS = 1 - Settings.ALIASING_FUNCTION_POINTERS # flip the test + self.do_run(src, '''Hello 7 from JS!''') + + def test_embind(self): + if self.emcc_args is None: return self.skip('requires emcc') + Building.COMPILER_TEST_OPTS += ['--bind'] + + src = r''' + #include + #include + + using namespace emscripten; + + int main() { + val Math = val::global("Math"); + + // two ways to call Math.abs + printf("abs(-10): %d\n", Math.call("abs", -10)); + printf("abs(-11): %d\n", Math["abs"](-11).as()); + + return 0; + } + ''' + self.do_run(src, 'abs(-10): 10\nabs(-11): 11'); + + def test_embind_2(self): + if self.emcc_args is None: return self.skip('requires emcc') + Building.COMPILER_TEST_OPTS += ['--bind', '--post-js', 'post.js'] + open('post.js', 'w').write(''' + Module.print('lerp ' + Module.lerp(1, 2, 0.66) + '.'); + ''') + src = r''' + #include + #include + #include + using namespace emscripten; + float lerp(float a, float b, float t) { + return (1 - t) * a + t * b; + } + EMSCRIPTEN_BINDINGS(my_module) { + function("lerp", &lerp); + } + ''' + self.do_run(src, 'lerp 1.66'); + + def test_scriptaclass(self): + if self.emcc_args is None: return self.skip('requires emcc') + + Settings.EXPORT_BINDINGS = 1 + + header_filename = os.path.join(self.get_dir(), 'header.h') + header = ''' + struct ScriptMe { + int value; + ScriptMe(int val); + int getVal(); // XXX Sadly, inlining these will result in LLVM not + // producing any code for them (when just building + // as a library) + void mulVal(int mul); + }; + ''' + h = open(header_filename, 'w') + h.write(header) + h.close() + + src = ''' + #include "header.h" + + ScriptMe::ScriptMe(int val) : value(val) { } + int ScriptMe::getVal() { return value; } + void ScriptMe::mulVal(int mul) { value *= mul; } + ''' + + # Way 1: use demangler and namespacer + + script_src = ''' + var sme = Module._.ScriptMe.__new__(83); // malloc(sizeof(ScriptMe)), ScriptMe::ScriptMe(sme, 83) / new ScriptMe(83) (at addr sme) + Module._.ScriptMe.mulVal(sme, 2); // ScriptMe::mulVal(sme, 2) sme.mulVal(2) + Module.print('*' + Module._.ScriptMe.getVal(sme) + '*'); + _free(sme); + Module.print('*ok*'); + ''' + post = ''' +def process(filename): + Popen([PYTHON, DEMANGLER, filename], stdout=open(filename + '.tmp', 'w')).communicate() + Popen([PYTHON, NAMESPACER, filename, filename + '.tmp'], stdout=open(filename + '.tmp2', 'w')).communicate() + src = open(filename, 'r').read().replace( + '// {{MODULE_ADDITIONS}', + 'Module["_"] = ' + open(filename + '.tmp2', 'r').read().replace('var ModuleNames = ', '').rstrip() + ';\n\n' + script_src + '\n\n' + + '// {{MODULE_ADDITIONS}' + ) + open(filename, 'w').write(src) +''' + # 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 and 'ASM_JS=0' not in self.emcc_args: # without asm, closure minifies Math.imul badly + self.emcc_args += ['--closure', '1'] # Use closure here, to test we export things right + + # Way 2: use CppHeaderParser + + Settings.RUNTIME_TYPE_INFO = 1 + + header = ''' + #include + + class Parent { + protected: + int value; + public: + Parent(int val); + Parent(Parent *p, Parent *q); // overload constructor + int getVal() { return value; }; // inline should work just fine here, unlike Way 1 before + void mulVal(int mul); + }; + + class Child1 : public Parent { + public: + Child1() : Parent(7) { printf("Child1:%d\\n", value); }; + Child1(int val) : Parent(val*2) { value -= 1; printf("Child1:%d\\n", value); }; + int getValSqr() { return value*value; } + int getValSqr(int more) { return value*value*more; } + int getValTimes(int times=1) { return value*times; } + }; + + class Child2 : public Parent { + public: + Child2() : Parent(9) { printf("Child2:%d\\n", value); }; + int getValCube() { return value*value*value; } + static void printStatic() { printf("*static*\\n"); } + + virtual void virtualFunc() { printf("*virtualf*\\n"); } + virtual void virtualFunc2() { printf("*virtualf2*\\n"); } + static void runVirtualFunc(Child2 *self) { self->virtualFunc(); }; + private: + void doSomethingSecret() { printf("security breached!\\n"); }; // we should not be able to do this + }; + ''' + open(header_filename, 'w').write(header) + + basename = os.path.join(self.get_dir(), 'bindingtest') + output = Popen([PYTHON, 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" + + Parent::Parent(int val) : value(val) { printf("Parent:%d\\n", val); } + Parent::Parent(Parent *p, Parent *q) : value(p->value + q->value) { printf("Parent:%d\\n", value); } + void Parent::mulVal(int mul) { value *= mul; } + + #include "bindingtest.cpp" + ''' + + post2 = ''' +def process(filename): + src = open(filename, 'a') + src.write(open('bindingtest.js').read() + '\\n\\n') + src.close() +''' + + def post3(filename): + script_src_2 = ''' + var sme = new Module.Parent(42); + sme.mulVal(2); + Module.print('*') + Module.print(sme.getVal()); + + Module.print('c1'); + + var c1 = new Module.Child1(); + Module.print(c1.getVal()); + c1.mulVal(2); + Module.print(c1.getVal()); + Module.print(c1.getValSqr()); + Module.print(c1.getValSqr(3)); + Module.print(c1.getValTimes()); // default argument should be 1 + Module.print(c1.getValTimes(2)); + + Module.print('c1 v2'); + + c1 = new Module.Child1(8); // now with a parameter, we should handle the overloading automatically and properly and use constructor #2 + Module.print(c1.getVal()); + c1.mulVal(2); + Module.print(c1.getVal()); + Module.print(c1.getValSqr()); + Module.print(c1.getValSqr(3)); + + Module.print('c2') + + var c2 = new Module.Child2(); + Module.print(c2.getVal()); + c2.mulVal(2); + Module.print(c2.getVal()); + Module.print(c2.getValCube()); + var succeeded; + try { + succeeded = 0; + Module.print(c2.doSomethingSecret()); // should fail since private + succeeded = 1; + } catch(e) {} + Module.print(succeeded); + try { + succeeded = 0; + Module.print(c2.getValSqr()); // function from the other class + succeeded = 1; + } catch(e) {} + Module.print(succeeded); + try { + succeeded = 0; + c2.getValCube(); // sanity + succeeded = 1; + } catch(e) {} + Module.print(succeeded); + + Module.Child2.prototype.printStatic(); // static calls go through the prototype + + // virtual function + c2.virtualFunc(); + Module.Child2.prototype.runVirtualFunc(c2); + c2.virtualFunc2(); + + // extend the class from JS + var c3 = new Module.Child2; + Module.customizeVTable(c3, [{ + original: Module.Child2.prototype.virtualFunc, + replacement: function() { + Module.print('*js virtualf replacement*'); + } + }, { + original: Module.Child2.prototype.virtualFunc2, + replacement: function() { + Module.print('*js virtualf2 replacement*'); + } + }]); + c3.virtualFunc(); + Module.Child2.prototype.runVirtualFunc(c3); + c3.virtualFunc2(); + + c2.virtualFunc(); // original should remain the same + Module.Child2.prototype.runVirtualFunc(c2); + c2.virtualFunc2(); + Module.print('*ok*'); + ''' + code = open(filename).read() + src = open(filename, 'w') + src.write('var Module = {};\n') # name Module + src.write(code) + src.write(script_src_2 + '\n') + src.close() + + Settings.RESERVED_FUNCTION_POINTERS = 20 + + self.do_run(src, '''* +84 +c1 +Parent:7 +Child1:7 +7 +14 +196 +588 +14 +28 +c1 v2 +Parent:16 +Child1:15 +15 +30 +900 +2700 +c2 +Parent:9 +Child2:9 +9 +18 +5832 +0 +0 +1 +*static* +*virtualf* +*virtualf* +*virtualf2*''' + (''' +Parent:9 +Child2:9 +*js virtualf replacement* +*js virtualf replacement* +*js virtualf2 replacement* +*virtualf* +*virtualf* +*virtualf2*''') + ''' +*ok* +''', post_build=(post2, post3)) + + def test_scriptaclass_2(self): + if self.emcc_args is None: return self.skip('requires emcc') + + Settings.EXPORT_BINDINGS = 1 + + header_filename = os.path.join(self.get_dir(), 'header.h') + header = ''' + #include + #include + + 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([PYTHON, 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') + + Settings.RUNTIME_TYPE_INFO = 1 + if Settings.QUANTUM_SIZE != 4: return self.skip('We assume normal sizes in the output here') + + src = ''' + #include + struct UserStruct { + int x; + char y; + short z; + }; + struct Encloser { + short x; + UserStruct us; + int y; + }; + int main() { + Encloser e; + e.us.y = 5; + printf("*ok:%d*\\n", e.us.y); + return 0; + } + ''' + + post = ''' +def process(filename): + src = open(filename, 'r').read().replace( + '// {{POST_RUN_ADDITIONS}}', + \'\'\' + if (Runtime.typeInfo) { + Module.print('|' + Runtime.typeInfo.UserStruct.fields + '|' + Runtime.typeInfo.UserStruct.flatIndexes + '|'); + var t = Runtime.generateStructInfo(['x', { us: ['x', 'y', 'z'] }, 'y'], 'Encloser') + Module.print('|' + [t.x, t.us.x, t.us.y, t.us.z, t.y] + '|'); + Module.print('|' + JSON.stringify(Runtime.generateStructInfo(['x', 'y', 'z'], 'UserStruct')) + '|'); + } else { + Module.print('No type info.'); + } + \'\'\' + ) + open(filename, 'w').write(src) +''' + + self.do_run(src, + '*ok:5*\n|i32,i8,i16|0,4,6|\n|0,4,8,10,12|\n|{"__size__":8,"x":0,"y":4,"z":6}|', + post_build=post) + + # Make sure that without the setting, we don't spam the .js with the type info + Settings.RUNTIME_TYPE_INFO = 0 + self.do_run(src, 'No type info.', post_build=post) + + ### Tests for tools + + def test_safe_heap(self): + if not Settings.SAFE_HEAP: return self.skip('We need SAFE_HEAP to test SAFE_HEAP') + if Settings.USE_TYPED_ARRAYS == 2: return self.skip('It is ok to violate the load-store assumption with TA2') + if Building.LLVM_OPTS: return self.skip('LLVM can optimize away the intermediate |x|') + + src = ''' + #include + #include + int main() { int *x = (int*)malloc(sizeof(int)); + *x = 20; + float *y = (float*)x; + printf("%f\\n", *y); + printf("*ok*\\n"); + return 0; + } + ''' + + try: + self.do_run(src, '*nothingatall*') + except Exception, e: + # This test *should* fail, by throwing this exception + assert 'Assertion failed: Load-store consistency assumption failure!' in str(e), str(e) + + # And we should not fail if we disable checking on that line + + Settings.SAFE_HEAP = 3 + Settings.SAFE_HEAP_LINES = ["src.cpp:7"] + + self.do_run(src, '*ok*') + + # But if we disable the wrong lines, we still fail + + Settings.SAFE_HEAP_LINES = ["src.cpp:99"] + + try: + self.do_run(src, '*nothingatall*') + except Exception, e: + # This test *should* fail, by throwing this exception + assert 'Assertion failed: Load-store consistency assumption failure!' in str(e), str(e) + + # And reverse the checks with = 2 + + Settings.SAFE_HEAP = 2 + Settings.SAFE_HEAP_LINES = ["src.cpp:99"] + + self.do_run(src, '*ok*') + + Settings.SAFE_HEAP = 1 + + # Linking multiple files should work too + + module = ''' + #include + #include + void callFunc() { int *x = (int*)malloc(sizeof(int)); + *x = 20; + float *y = (float*)x; + printf("%f\\n", *y); + } + ''' + module_name = os.path.join(self.get_dir(), 'module.cpp') + open(module_name, 'w').write(module) + + main = ''' + #include + #include + extern void callFunc(); + int main() { callFunc(); + int *x = (int*)malloc(sizeof(int)); + *x = 20; + float *y = (float*)x; + printf("%f\\n", *y); + printf("*ok*\\n"); + return 0; + } + ''' + main_name = os.path.join(self.get_dir(), 'main.cpp') + open(main_name, 'w').write(main) + + Building.emcc(module_name, ['-g']) + Building.emcc(main_name, ['-g']) + all_name = os.path.join(self.get_dir(), 'all.bc') + Building.link([module_name + '.o', main_name + '.o'], all_name) + + try: + self.do_ll_run(all_name, '*nothingatall*') + except Exception, e: + # This test *should* fail, by throwing this exception + assert 'Assertion failed: Load-store consistency assumption failure!' in str(e), str(e) + + # And we should not fail if we disable checking on those lines + + Settings.SAFE_HEAP = 3 + Settings.SAFE_HEAP_LINES = ["module.cpp:7", "main.cpp:9"] + + self.do_ll_run(all_name, '*ok*') + + # But we will fail if we do not disable exactly what we need to - any mistake leads to error + + for lines in [["module.cpp:22", "main.cpp:9"], ["module.cpp:7", "main.cpp:29"], ["module.cpp:127", "main.cpp:449"], ["module.cpp:7"], ["main.cpp:9"]]: + Settings.SAFE_HEAP_LINES = lines + try: + self.do_ll_run(all_name, '*nothingatall*') + except Exception, e: + # This test *should* fail, by throwing this exception + assert 'Assertion failed: Load-store consistency assumption failure!' in str(e), str(e) + + def test_debug(self): + if '-g' not in Building.COMPILER_TEST_OPTS: Building.COMPILER_TEST_OPTS.append('-g') + if self.emcc_args is not None: + if '-O1' in self.emcc_args or '-O2' in self.emcc_args: return self.skip('optimizations remove LLVM debug info') + + src = ''' + #include + #include + + void checker(int x) { + x += 20; + assert(x < 15); // this is line 7! + } + + int main() { + checker(10); + return 0; + } + ''' + try: + self.do_run(src, '*nothingatall*') + except Exception, e: + # This test *should* fail + assert 'Assertion failed: x < 15' in str(e), str(e) + + lines = open('src.cpp.o.js', 'r').readlines() + lines = filter(lambda line: '___assert_fail(' in line or '___assert_func(' in line, lines) + found_line_num = any(('//@line 7 "' in line) for line in lines) + found_filename = any(('src.cpp"\n' in line) for line in lines) + assert found_line_num, 'Must have debug info with the line number' + assert found_filename, 'Must have debug info with the filename' + + def test_source_map(self): + if Settings.USE_TYPED_ARRAYS != 2: return self.skip("doesn't pass without typed arrays") + if NODE_JS not in JS_ENGINES: return self.skip('sourcemapper requires Node to run') + if '-g' not in Building.COMPILER_TEST_OPTS: Building.COMPILER_TEST_OPTS.append('-g') + + src = ''' + #include + #include + + __attribute__((noinline)) int foo() { + printf("hi"); // line 6 + return 1; // line 7 + } + + int main() { + printf("%d", foo()); // line 11 + return 0; // line 12 + } + ''' + + dirname = self.get_dir() + src_filename = os.path.join(dirname, 'src.cpp') + out_filename = os.path.join(dirname, 'a.out.js') + no_maps_filename = os.path.join(dirname, 'no-maps.out.js') + + with open(src_filename, 'w') as f: f.write(src) + assert '-g4' not in Building.COMPILER_TEST_OPTS + Building.emcc(src_filename, Settings.serialize() + self.emcc_args + + Building.COMPILER_TEST_OPTS, out_filename) + # the file name may find its way into the generated code, so make sure we + # can do an apples-to-apples comparison by compiling with the same file name + shutil.move(out_filename, no_maps_filename) + with open(no_maps_filename) as f: no_maps_file = f.read() + no_maps_file = re.sub(' *//@.*$', '', no_maps_file, flags=re.MULTILINE) + Building.COMPILER_TEST_OPTS.append('-g4') + + def build_and_check(): + import json + Building.emcc(src_filename, Settings.serialize() + self.emcc_args + + Building.COMPILER_TEST_OPTS, out_filename, stderr=PIPE) + with open(out_filename) as f: out_file = f.read() + # after removing the @line and @sourceMappingURL comments, the build + # result should be identical to the non-source-mapped debug version. + # this is worth checking because the parser AST swaps strings for token + # objects when generating source maps, so we want to make sure the + # optimizer can deal with both types. + out_file = re.sub(' *//@.*$', '', out_file, flags=re.MULTILINE) + def clean(code): + return code.replace('{\n}', '{}') + self.assertIdentical(clean(no_maps_file), clean(out_file)) + map_filename = out_filename + '.map' + data = json.load(open(map_filename, 'r')) + self.assertIdentical(out_filename, data['file']) + self.assertIdentical(src_filename, data['sources'][0]) + self.assertIdentical(src, data['sourcesContent'][0]) + mappings = json.loads(jsrun.run_js( + path_from_root('tools', 'source-maps', 'sourcemap2json.js'), + tools.shared.NODE_JS, [map_filename])) + seen_lines = set() + for m in mappings: + self.assertIdentical(src_filename, m['source']) + seen_lines.add(m['originalLine']) + # ensure that all the 'meaningful' lines in the original code get mapped + assert seen_lines.issuperset([6, 7, 11, 12]) + + # EMCC_DEBUG=2 causes lots of intermediate files to be written, and so + # serves as a stress test for source maps because it needs to correlate + # line numbers across all those files. + old_emcc_debug = os.environ.get('EMCC_DEBUG', None) + os.environ.pop('EMCC_DEBUG', None) + try: + build_and_check() + os.environ['EMCC_DEBUG'] = '2' + build_and_check() + finally: + if old_emcc_debug is not None: + os.environ['EMCC_DEBUG'] = old_emcc_debug + else: + os.environ.pop('EMCC_DEBUG', None) + + def test_exception_source_map(self): + if Settings.USE_TYPED_ARRAYS != 2: return self.skip("doesn't pass without typed arrays") + if '-g4' not in Building.COMPILER_TEST_OPTS: Building.COMPILER_TEST_OPTS.append('-g4') + if NODE_JS not in JS_ENGINES: return self.skip('sourcemapper requires Node to run') + + src = ''' + #include + + __attribute__((noinline)) void foo(int i) { + if (i < 10) throw i; // line 5 + } + + int main() { + int i; + scanf("%d", &i); + foo(i); + return 0; + } + ''' + + def post(filename): + import json + map_filename = filename + '.map' + mappings = json.loads(jsrun.run_js( + path_from_root('tools', 'source-maps', 'sourcemap2json.js'), + tools.shared.NODE_JS, [map_filename])) + with open(filename) as f: lines = f.readlines() + for m in mappings: + if m['originalLine'] == 5 and '__cxa_throw' in lines[m['generatedLine']]: + return + assert False, 'Must label throw statements with line numbers' + + dirname = self.get_dir() + self.build(src, dirname, os.path.join(dirname, 'src.cpp'), post_build=(None, post)) + + def test_linespecific(self): + if Settings.ASM_JS: return self.skip('asm always has corrections on') + + if '-g' not in Building.COMPILER_TEST_OPTS: Building.COMPILER_TEST_OPTS.append('-g') + if self.emcc_args: + self.emcc_args += ['--llvm-opts', '0'] # llvm full opts make the expected failures here not happen + Building.COMPILER_TEST_OPTS += ['--llvm-opts', '0'] + + Settings.CHECK_SIGNS = 0 + Settings.CHECK_OVERFLOWS = 0 + + # Signs + + src = ''' + #include + #include + + int main() + { + int varey = 100; + unsigned int MAXEY = -1; + printf("*%d*\\n", varey >= MAXEY); // 100 >= -1? not in unsigned! + } + ''' + + Settings.CORRECT_SIGNS = 0 + self.do_run(src, '*1*') # This is a fail - we expect 0 + + Settings.CORRECT_SIGNS = 1 + self.do_run(src, '*0*') # Now it will work properly + + # And now let's fix just that one line + Settings.CORRECT_SIGNS = 2 + Settings.CORRECT_SIGNS_LINES = ["src.cpp:9"] + self.do_run(src, '*0*') + + # Fixing the wrong line should not work + Settings.CORRECT_SIGNS = 2 + Settings.CORRECT_SIGNS_LINES = ["src.cpp:3"] + self.do_run(src, '*1*') + + # And reverse the checks with = 2 + Settings.CORRECT_SIGNS = 3 + Settings.CORRECT_SIGNS_LINES = ["src.cpp:3"] + self.do_run(src, '*0*') + Settings.CORRECT_SIGNS = 3 + Settings.CORRECT_SIGNS_LINES = ["src.cpp:9"] + self.do_run(src, '*1*') + + Settings.CORRECT_SIGNS = 0 + + # Overflows + + src = ''' + #include + int main() { + int t = 77; + for (int i = 0; i < 30; i++) { + t = t + t + t + t + t + 1; + } + printf("*%d,%d*\\n", t, t & 127); + return 0; + } + ''' + + correct = '*186854335,63*' + Settings.CORRECT_OVERFLOWS = 0 + try: + self.do_run(src, correct) + raise Exception('UNEXPECTED-PASS') + except Exception, e: + assert 'UNEXPECTED' not in str(e), str(e) + assert 'Expected to find' in str(e), str(e) + + Settings.CORRECT_OVERFLOWS = 1 + self.do_run(src, correct) # Now it will work properly + + # And now let's fix just that one line + Settings.CORRECT_OVERFLOWS = 2 + Settings.CORRECT_OVERFLOWS_LINES = ["src.cpp:6"] + self.do_run(src, correct) + + # Fixing the wrong line should not work + Settings.CORRECT_OVERFLOWS = 2 + Settings.CORRECT_OVERFLOWS_LINES = ["src.cpp:3"] + try: + self.do_run(src, correct) + raise Exception('UNEXPECTED-PASS') + except Exception, e: + assert 'UNEXPECTED' not in str(e), str(e) + assert 'Expected to find' in str(e), str(e) + + # And reverse the checks with = 2 + Settings.CORRECT_OVERFLOWS = 3 + Settings.CORRECT_OVERFLOWS_LINES = ["src.cpp:3"] + self.do_run(src, correct) + Settings.CORRECT_OVERFLOWS = 3 + Settings.CORRECT_OVERFLOWS_LINES = ["src.cpp:6"] + try: + self.do_run(src, correct) + raise Exception('UNEXPECTED-PASS') + except Exception, e: + assert 'UNEXPECTED' not in str(e), str(e) + assert 'Expected to find' in str(e), str(e) + + Settings.CORRECT_OVERFLOWS = 0 + + # Roundings + + src = ''' + #include + #include + + int main() + { + TYPE x = -5; + printf("*%d*", x/2); + x = 5; + printf("*%d*", x/2); + + float y = -5.33; + x = y; + printf("*%d*", x); + y = 5.33; + x = y; + printf("*%d*", x); + + printf("\\n"); + } + ''' + + if Settings.USE_TYPED_ARRAYS != 2: # the errors here are very specific to non-i64 mode 1 + Settings.CORRECT_ROUNDINGS = 0 + self.do_run(src.replace('TYPE', 'long long'), '*-3**2**-6**5*') # JS floor operations, always to the negative. This is an undetected error here! + self.do_run(src.replace('TYPE', 'int'), '*-2**2**-5**5*') # We get these right, since they are 32-bit and we can shortcut using the |0 trick + self.do_run(src.replace('TYPE', 'unsigned int'), '*-2**2**-6**5*') + + Settings.CORRECT_ROUNDINGS = 1 + Settings.CORRECT_SIGNS = 1 # To be correct here, we need sign corrections as well + self.do_run(src.replace('TYPE', 'long long'), '*-2**2**-5**5*') # Correct + self.do_run(src.replace('TYPE', 'int'), '*-2**2**-5**5*') # Correct + self.do_run(src.replace('TYPE', 'unsigned int'), '*2147483645**2**-5**5*') # Correct + Settings.CORRECT_SIGNS = 0 + + if Settings.USE_TYPED_ARRAYS != 2: # the errors here are very specific to non-i64 mode 1 + Settings.CORRECT_ROUNDINGS = 2 + Settings.CORRECT_ROUNDINGS_LINES = ["src.cpp:13"] # Fix just the last mistake + self.do_run(src.replace('TYPE', 'long long'), '*-3**2**-5**5*') + self.do_run(src.replace('TYPE', 'int'), '*-2**2**-5**5*') # Here we are lucky and also get the first one right + self.do_run(src.replace('TYPE', 'unsigned int'), '*-2**2**-5**5*') + + # And reverse the check with = 2 + if Settings.USE_TYPED_ARRAYS != 2: # the errors here are very specific to non-i64 mode 1 + Settings.CORRECT_ROUNDINGS = 3 + Settings.CORRECT_ROUNDINGS_LINES = ["src.cpp:999"] + self.do_run(src.replace('TYPE', 'long long'), '*-2**2**-5**5*') + self.do_run(src.replace('TYPE', 'int'), '*-2**2**-5**5*') + Settings.CORRECT_SIGNS = 1 # To be correct here, we need sign corrections as well + self.do_run(src.replace('TYPE', 'unsigned int'), '*2147483645**2**-5**5*') + Settings.CORRECT_SIGNS = 0 + + def test_exit_status(self): + if self.emcc_args is None: return self.skip('need emcc') + src = r''' + #include + #include + static void cleanup() { + printf("cleanup\n"); + } + + int main() { + atexit(cleanup); // this atexit should still be called + printf("hello, world!\n"); + exit(118); // Unusual exit status to make sure it's working! + } + ''' + open('post.js', 'w').write(''' + Module.addOnExit(function () { + Module.print('I see exit status: ' + EXITSTATUS); + }); + Module.callMain(); + ''') + self.emcc_args += ['-s', 'INVOKE_RUN=0', '--post-js', 'post.js'] + self.do_run(src, 'hello, world!\nexit(118) called\ncleanup\nI see exit status: 118') + + def test_gc(self): + if self.emcc_args == None: return self.skip('needs ta2') + if Settings.ASM_JS: return self.skip('asm cannot support generic function table') + + Settings.GC_SUPPORT = 1 + + src = r''' + #include + #include + #include + + void *global; + + void finalizer(void *ptr, void *arg) { + printf("finalizing %d (global == %d)\n", (int)arg, ptr == global); + } + + void finalizer2(void *ptr, void *arg) { + printf("finalizing2 %d (global == %d)\n", (int)arg, ptr == global); + } + + int main() { + GC_INIT(); + + void *local, *local2, *local3, *local4, *local5, *local6; + + // Hold on to global, drop locals + + global = GC_MALLOC(1024); // rooted since in a static allocation + GC_REGISTER_FINALIZER_NO_ORDER(global, finalizer, 0, 0, 0); + printf("alloc %p\n", global); + + local = GC_MALLOC(1024); // not rooted since stack is not scanned + GC_REGISTER_FINALIZER_NO_ORDER(local, finalizer, (void*)1, 0, 0); + printf("alloc %p\n", local); + + assert((char*)local - (char*)global >= 1024 || (char*)global - (char*)local >= 1024); + + local2 = GC_MALLOC(1024); // no finalizer + printf("alloc %p\n", local2); + + local3 = GC_MALLOC(1024); // with finalizable2 + GC_REGISTER_FINALIZER_NO_ORDER(local3, finalizer2, (void*)2, 0, 0); + printf("alloc %p\n", local); + + local4 = GC_MALLOC(1024); // yet another + GC_REGISTER_FINALIZER_NO_ORDER(local4, finalizer2, (void*)3, 0, 0); + printf("alloc %p\n", local); + + printf("basic test\n"); + + GC_FORCE_COLLECT(); + + printf("*\n"); + + GC_FREE(global); // force free will actually work + + // scanning inside objects + + global = GC_MALLOC(12); + GC_REGISTER_FINALIZER_NO_ORDER(global, finalizer, 0, 0, 0); + local = GC_MALLOC(12); + GC_REGISTER_FINALIZER_NO_ORDER(local, finalizer, (void*)1, 0, 0); + local2 = GC_MALLOC_ATOMIC(12); + GC_REGISTER_FINALIZER_NO_ORDER(local2, finalizer, (void*)2, 0, 0); + local3 = GC_MALLOC(12); + GC_REGISTER_FINALIZER_NO_ORDER(local3, finalizer, (void*)3, 0, 0); + local4 = GC_MALLOC(12); + GC_REGISTER_FINALIZER_NO_ORDER(local4, finalizer, (void*)4, 0, 0); + local5 = GC_MALLOC_UNCOLLECTABLE(12); + // This should never trigger since local5 is uncollectable + GC_REGISTER_FINALIZER_NO_ORDER(local5, finalizer, (void*)5, 0, 0); + + printf("heap size = %d\n", GC_get_heap_size()); + + local4 = GC_REALLOC(local4, 24); + + printf("heap size = %d\n", GC_get_heap_size()); + + local6 = GC_MALLOC(12); + GC_REGISTER_FINALIZER_NO_ORDER(local6, finalizer, (void*)6, 0, 0); + // This should be the same as a free + GC_REALLOC(local6, 0); + + void **globalData = (void**)global; + globalData[0] = local; + globalData[1] = local2; + + void **localData = (void**)local; + localData[0] = local3; + + void **local2Data = (void**)local2; + local2Data[0] = local4; // actually ignored, because local2 is atomic, so 4 is freeable + + printf("object scan test test\n"); + + GC_FORCE_COLLECT(); + + printf("*\n"); + + GC_FREE(global); // force free will actually work + + printf("*\n"); + + GC_FORCE_COLLECT(); + + printf(".\n"); + + global = 0; + + return 0; + } + ''' + self.do_run(src, '''basic test +finalizing 1 (global == 0) +finalizing2 2 (global == 0) +finalizing2 3 (global == 0) +* +finalizing 0 (global == 1) +heap size = 72 +heap size = 84 +finalizing 6 (global == 0) +object scan test test +finalizing 4 (global == 0) +* +finalizing 0 (global == 1) +* +finalizing 1 (global == 0) +finalizing 2 (global == 0) +finalizing 3 (global == 0) +. +''') + +# Generate tests for everything +def make_run(fullname, name=-1, compiler=-1, embetter=0, quantum_size=0, + typed_arrays=0, emcc_args=None, env=None): + + if env is None: env = {} + + TT = type(fullname, (T,), dict(run_name = fullname, env = env)) + + def tearDown(self): + super(TT, self).tearDown() + + for k, v in self.env.iteritems(): + del os.environ[k] + + # clear global changes to Building + Building.COMPILER_TEST_OPTS = [] + Building.COMPILER = CLANG + Building.LLVM_OPTS = 0 + + TT.tearDown = tearDown + + def setUp(self): + super(TT, self).setUp() + for k, v in self.env.iteritems(): + assert k not in os.environ, k + ' should not be in environment' + os.environ[k] = v + + global checked_sanity + if not checked_sanity: + print '(checking sanity from test runner)' # do this after we set env stuff + check_sanity(force=True) + checked_sanity = True + + Building.COMPILER_TEST_OPTS = ['-g'] + os.chdir(self.get_dir()) # Ensure the directory exists and go there + Building.COMPILER = compiler + + self.emcc_args = None if emcc_args is None else emcc_args[:] + if self.emcc_args is not None: + Settings.load(self.emcc_args) + Building.LLVM_OPTS = 0 + if '-O2' in self.emcc_args: + Building.COMPILER_TEST_OPTS = [] # remove -g in -O2 tests, for more coverage + #Building.COMPILER_TEST_OPTS += self.emcc_args + for arg in self.emcc_args: + if arg.startswith('-O'): + Building.COMPILER_TEST_OPTS.append(arg) # so bitcode is optimized too, this is for cpp to ll + else: + try: + key, value = arg.split('=') + Settings[key] = value # forward -s K=V + except: + pass + return + + # TODO: Move much of these to a init() function in shared.py, and reuse that + Settings.USE_TYPED_ARRAYS = typed_arrays + Settings.INVOKE_RUN = 1 + Settings.RELOOP = 0 # we only do them in the "o2" pass + Settings.MICRO_OPTS = embetter + Settings.QUANTUM_SIZE = quantum_size + Settings.ASSERTIONS = 1-embetter + Settings.SAFE_HEAP = 1-embetter + Settings.CHECK_OVERFLOWS = 1-embetter + Settings.CORRECT_OVERFLOWS = 1-embetter + Settings.CORRECT_SIGNS = 0 + Settings.CORRECT_ROUNDINGS = 0 + Settings.CORRECT_OVERFLOWS_LINES = CORRECT_SIGNS_LINES = CORRECT_ROUNDINGS_LINES = SAFE_HEAP_LINES = [] + Settings.CHECK_SIGNS = 0 #1-embetter + Settings.RUNTIME_TYPE_INFO = 0 + Settings.DISABLE_EXCEPTION_CATCHING = 0 + Settings.INCLUDE_FULL_LIBRARY = 0 + Settings.BUILD_AS_SHARED_LIB = 0 + Settings.RUNTIME_LINKED_LIBS = [] + Settings.EMULATE_UNALIGNED_ACCESSES = int(Settings.USE_TYPED_ARRAYS == 2 and Building.LLVM_OPTS == 2) + Settings.DOUBLE_MODE = 1 if Settings.USE_TYPED_ARRAYS and Building.LLVM_OPTS == 0 else 0 + Settings.PRECISE_I64_MATH = 0 + Settings.NAMED_GLOBALS = 0 if not embetter else 1 + + TT.setUp = setUp + + return TT + +# Make one run with the defaults +default = make_run("default", compiler=CLANG, emcc_args=[]) + +# Make one run with -O1, with safe heap +o1 = make_run("o1", compiler=CLANG, emcc_args=["-O1", "-s", "ASM_JS=0", "-s", "SAFE_HEAP=1"]) + +# Make one run with -O2, but without closure (we enable closure in specific tests, otherwise on everything it is too slow) +o2 = make_run("o2", compiler=CLANG, emcc_args=["-O2", "-s", "ASM_JS=0", "-s", "JS_CHUNK_SIZE=1024"]) + +# asm.js +asm1 = make_run("asm1", compiler=CLANG, emcc_args=["-O1", "-s", "CHECK_HEAP_ALIGN=1"]) +asm2 = make_run("asm2", compiler=CLANG, emcc_args=["-O2"]) +asm2g = make_run("asm2g", compiler=CLANG, emcc_args=["-O2", "-g", "-s", "ASSERTIONS=1", "--memory-init-file", "1"]) +asm2x86 = make_run("asm2x86", compiler=CLANG, emcc_args=["-O2", "-g", "-s", "CHECK_HEAP_ALIGN=1"], env={"EMCC_LLVM_TARGET": "i386-pc-linux-gnu"}) + +# Make custom runs with various options +for compiler, quantum, embetter, typed_arrays in [ + (CLANG, 4, 0, 0), + (CLANG, 4, 1, 1), +]: + fullname = 's_0_%d%s%s' % ( + embetter, '' if quantum == 4 else '_q' + str(quantum), '' if typed_arrays in [0, 1] else '_t' + str(typed_arrays) + ) + locals()[fullname] = make_run(fullname, fullname, compiler, embetter, quantum, typed_arrays) + +del T # T is just a shape for the specific subclasses, we don't test it itself \ No newline at end of file diff --git a/tests/test_other.py b/tests/test_other.py new file mode 100644 index 0000000000000..b3e8a2a179a5e --- /dev/null +++ b/tests/test_other.py @@ -0,0 +1,1913 @@ +import multiprocessing, os, re, shutil, subprocess, sys +import tools.shared +from tools.shared import * +from runner import RunnerCore, path_from_root + +class other(RunnerCore): + def test_emcc(self): + for compiler in [EMCC, EMXX]: + shortcompiler = os.path.basename(compiler) + suffix = '.c' if compiler == EMCC else '.cpp' + + # --version + output = Popen([PYTHON, compiler, '--version'], stdout=PIPE, stderr=PIPE).communicate() + output = output[0].replace('\r', '') + self.assertContained('''emcc (Emscripten GCC-like replacement)''', output) + self.assertContained('''Copyright (C) 2013 the Emscripten authors (see AUTHORS.txt) +This is free and open source software under the MIT license. +There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +''', output) + + # -v, without input files + output = Popen([PYTHON, compiler, '-v'], stdout=PIPE, stderr=PIPE).communicate() + self.assertContained('''clang version''', output[1].replace('\r', ''), output[1].replace('\r', '')) + + # --help + output = Popen([PYTHON, compiler, '--help'], stdout=PIPE, stderr=PIPE).communicate() + self.assertContained('''%s [options] file... + +Most normal gcc/g++ options will work, for example: + --help Display this information + --version Display compiler version information + +Options that are modified or new in %s include: + -O0 No optimizations (default) +''' % (shortcompiler, shortcompiler), output[0].replace('\r', ''), output[1].replace('\r', '')) + + # emcc src.cpp ==> writes a.out.js + self.clear() + output = Popen([PYTHON, compiler, path_from_root('tests', 'hello_world' + suffix)], stdout=PIPE, stderr=PIPE).communicate() + assert len(output[0]) == 0, output[0] + assert os.path.exists('a.out.js'), '\n'.join(output) + self.assertContained('hello, world!', run_js('a.out.js')) + + # properly report source code errors, and stop there + self.clear() + assert not os.path.exists('a.out.js') + process = Popen([PYTHON, compiler, path_from_root('tests', 'hello_world_error' + suffix)], stdout=PIPE, stderr=PIPE) + output = process.communicate() + assert not os.path.exists('a.out.js'), 'compilation failed, so no output file is expected' + assert len(output[0]) == 0, output[0] + assert process.returncode is not 0, 'Failed compilation must return a nonzero error code!' + self.assertNotContained('IOError', output[1]) # no python stack + self.assertNotContained('Traceback', output[1]) # no python stack + self.assertContained('error: invalid preprocessing directive', output[1]) + self.assertContained(["error: use of undeclared identifier 'cheez", "error: unknown type name 'cheez'"], output[1]) + self.assertContained('errors generated', output[1]) + assert 'compiler frontend failed to generate LLVM bitcode, halting' in output[1].split('errors generated.')[1] + + # emcc src.cpp -c and emcc src.cpp -o src.[o|bc] ==> should give a .bc file + # regression check: -o js should create "js", with bitcode content + for args in [['-c'], ['-o', 'src.o'], ['-o', 'src.bc'], ['-o', 'src.so'], ['-o', 'js']]: + target = args[1] if len(args) == 2 else 'hello_world.o' + self.clear() + Popen([PYTHON, compiler, path_from_root('tests', 'hello_world' + suffix)] + args, stdout=PIPE, stderr=PIPE).communicate() + syms = Building.llvm_nm(target) + assert len(syms.defs) == 1 and 'main' in syms.defs, 'Failed to generate valid bitcode' + if target == 'js': # make sure emcc can recognize the target as a bitcode file + shutil.move(target, target + '.bc') + target += '.bc' + output = Popen([PYTHON, compiler, target, '-o', target + '.js'], stdout = PIPE, stderr = PIPE).communicate() + assert len(output[0]) == 0, output[0] + assert os.path.exists(target + '.js'), 'Expected %s to exist since args are %s : %s' % (target + '.js', str(args), '\n'.join(output)) + self.assertContained('hello, world!', run_js(target + '.js')) + + # handle singleton archives + self.clear() + Popen([PYTHON, compiler, path_from_root('tests', 'hello_world' + suffix), '-o', 'a.bc'], stdout=PIPE, stderr=PIPE).communicate() + Popen([LLVM_AR, 'r', 'a.a', 'a.bc'], stdout=PIPE, stderr=PIPE).communicate() + assert os.path.exists('a.a') + output = Popen([PYTHON, compiler, 'a.a']).communicate() + assert os.path.exists('a.out.js'), output + self.assertContained('hello, world!', run_js('a.out.js')) + + # emcc src.ll ==> generates .js + self.clear() + output = Popen([PYTHON, compiler, path_from_root('tests', 'hello_world.ll')], stdout=PIPE, stderr=PIPE).communicate() + assert len(output[0]) == 0, output[0] + assert os.path.exists('a.out.js'), '\n'.join(output) + self.assertContained('hello, world!', run_js('a.out.js')) + + # emcc [..] -o [path] ==> should work with absolute paths + try: + for path in [os.path.abspath(os.path.join('..', 'file1.js')), os.path.join('b_dir', 'file2.js')]: + print path + self.clear(in_curr=True) + os.chdir(self.get_dir()) + if not os.path.exists('a_dir'): os.mkdir('a_dir') + os.chdir('a_dir') + if not os.path.exists('b_dir'): os.mkdir('b_dir') + output = Popen([PYTHON, compiler, path_from_root('tests', 'hello_world.ll'), '-o', path], stdout=PIPE, stderr=PIPE).communicate() + print output + assert os.path.exists(path), path + ' does not exist; ' + '\n'.join(output) + last = os.getcwd() + os.chdir(os.path.dirname(path)) + self.assertContained('hello, world!', run_js(os.path.basename(path))) + os.chdir(last) + finally: + os.chdir(self.get_dir()) + self.clear() + + # dlmalloc. dlmalloc is special in that it is the only part of libc that is (1) hard to write well, and + # very speed-sensitive. So we do not implement it in JS in library.js, instead we compile it from source + for source, has_malloc in [('hello_world' + suffix, False), ('hello_malloc.cpp', True)]: + print source, has_malloc + self.clear() + output = Popen([PYTHON, compiler, path_from_root('tests', source)], stdout=PIPE, stderr=PIPE).communicate() + assert os.path.exists('a.out.js'), '\n'.join(output) + self.assertContained('hello, world!', run_js('a.out.js')) + generated = open('a.out.js').read() + assert ('function _malloc(bytes) {' in generated) == (not has_malloc), 'If malloc is needed, it should be there, if not not' + + # Optimization: emcc src.cpp -o something.js [-Ox]. -O0 is the same as not specifying any optimization setting + for params, opt_level, bc_params, closure, has_malloc in [ # bc params are used after compiling to bitcode + (['-o', 'something.js'], 0, None, 0, 1), + (['-o', 'something.js', '-O0'], 0, None, 0, 0), + (['-o', 'something.js', '-O1'], 1, None, 0, 0), + (['-o', 'something.js', '-O1', '-g'], 1, None, 0, 0), # no closure since debug + (['-o', 'something.js', '-O1', '--closure', '1'], 1, None, 1, 0), + (['-o', 'something.js', '-O1', '--closure', '1', '-s', 'ASM_JS=0'], 1, None, 1, 0), + (['-o', 'something.js', '-O2'], 2, None, 0, 1), + (['-o', 'something.js', '-O2', '-g'], 2, None, 0, 0), + (['-o', 'something.js', '-Os'], 2, None, 0, 1), + (['-o', 'something.js', '-O3', '-s', 'ASM_JS=0'], 3, None, 1, 1), + # and, test compiling to bitcode first + (['-o', 'something.bc'], 0, [], 0, 0), + (['-o', 'something.bc', '-O0'], 0, [], 0, 0), + (['-o', 'something.bc', '-O1'], 1, ['-O1'], 0, 0), + (['-o', 'something.bc', '-O2'], 2, ['-O2'], 0, 0), + (['-o', 'something.bc', '-O3'], 3, ['-O3', '-s', 'ASM_JS=0'], 1, 0), + (['-O1', '-o', 'something.bc'], 1, [], 0, 0), + ]: + print params, opt_level, bc_params, closure, has_malloc + self.clear() + keep_debug = '-g' in params + args = [PYTHON, compiler, path_from_root('tests', 'hello_world_loop' + ('_malloc' if has_malloc else '') + '.cpp')] + params + print '..', args + output = Popen(args, + stdout=PIPE, stderr=PIPE).communicate() + assert len(output[0]) == 0, output[0] + if bc_params is not None: + assert os.path.exists('something.bc'), output[1] + bc_args = [PYTHON, compiler, 'something.bc', '-o', 'something.js'] + bc_params + print '....', bc_args + output = Popen(bc_args, stdout=PIPE, stderr=PIPE).communicate() + assert os.path.exists('something.js'), output[1] + assert ('Applying some potentially unsafe optimizations!' in output[1]) == (opt_level >= 3), 'unsafe warning should appear in opt >= 3' + self.assertContained('hello, world!', run_js('something.js')) + + # Verify optimization level etc. in the generated code + # XXX these are quite sensitive, and will need updating when code generation changes + generated = open('something.js').read() # TODO: parse out the _main function itself, not support code, if the tests below need that some day + assert 'new Uint16Array' in generated and 'new Uint32Array' in generated, 'typed arrays 2 should be used by default' + assert 'SAFE_HEAP' not in generated, 'safe heap should not be used by default' + assert ': while(' not in generated, 'when relooping we also js-optimize, so there should be no labelled whiles' + if closure: + if opt_level == 0: assert '._main =' in generated, 'closure compiler should have been run' + elif opt_level >= 1: assert '._main=' in generated, 'closure compiler should have been run (and output should be minified)' + else: + # closure has not been run, we can do some additional checks. TODO: figure out how to do these even with closure + assert '._main = ' not in generated, 'closure compiler should not have been run' + if keep_debug: + assert ('(label)' in generated or '(label | 0)' in generated) == (opt_level <= 1), 'relooping should be in opt >= 2' + assert ('assert(STACKTOP < STACK_MAX' in generated) == (opt_level == 0), 'assertions should be in opt == 0' + assert 'var $i;' in generated or 'var $i_0' in generated or 'var $storemerge3;' in generated or 'var $storemerge4;' in generated or 'var $i_04;' in generated or 'var $original = 0' in generated, 'micro opts should always be on' + if opt_level >= 2 and '-g' in params: + assert re.search('HEAP8\[\$?\w+ ?\+ ?\(+\$?\w+ ?', generated) or re.search('HEAP8\[HEAP32\[', generated), 'eliminator should create compound expressions, and fewer one-time vars' # also in -O1, but easier to test in -O2 + assert ('_puts(' in generated) == (opt_level >= 1), 'with opt >= 1, llvm opts are run and they should optimize printf to puts' + if opt_level == 0 or '-g' in params: assert 'function _main() {' in generated, 'Should be unminified, including whitespace' + elif opt_level >= 2: assert ('function _main(){' in generated or '"use asm";var a=' in generated), 'Should be whitespace-minified' + + # emcc -s RELOOP=1 src.cpp ==> should pass -s to emscripten.py. --typed-arrays is a convenient alias for -s USE_TYPED_ARRAYS + for params, test, text in [ + (['-O2'], lambda generated: 'function intArrayToString' in generated, 'shell has unminified utilities'), + (['-O2', '--closure', '1'], lambda generated: 'function intArrayToString' not in generated, 'closure minifies the shell'), + (['-O2'], lambda generated: 'var b=0' in generated and not 'function _main' in generated, 'registerize/minify is run by default in -O2'), + (['-O2', '--minify', '0'], lambda generated: 'var b = 0' in generated and not 'function _main' in generated, 'minify is cancelled, but not registerize'), + (['-O2', '--js-opts', '0'], lambda generated: 'var b=0' not in generated and 'var b = 0' not in generated and 'function _main' in generated, 'js opts are cancelled'), + (['-O2', '-g'], lambda generated: 'var b=0' not in generated and 'var b = 0' not in generated and 'function _main' in generated, 'registerize/minify is cancelled by -g'), + (['-O2', '-g0'], lambda generated: 'var b=0' in generated and not 'function _main' in generated, 'registerize/minify is run by default in -O2 -g0'), + (['-O2', '-g1'], lambda generated: 'var b = 0' in generated and not 'function _main' in generated, 'compress is cancelled by -g1'), + (['-O2', '-g2'], lambda generated: ('var b = 0' in generated or 'var i1 = 0' in generated) and 'function _main' in generated, 'minify is cancelled by -g2'), + (['-O2', '-g3'], lambda generated: 'var b=0' not in generated and 'var b = 0' not in generated and 'function _main' in generated, 'registerize is cancelled by -g3'), + #(['-O2', '-g4'], lambda generated: 'var b=0' not in generated and 'var b = 0' not in generated and 'function _main' in generated, 'same as -g3 for now'), + (['-s', 'INLINING_LIMIT=0'], lambda generated: 'function _dump' in generated, 'no inlining without opts'), + (['-O3', '-s', 'INLINING_LIMIT=0', '--closure', '0'], lambda generated: 'function _dump' not in generated, 'lto/inlining'), + (['-Os', '--llvm-lto', '1', '-s', 'ASM_JS=0'], lambda generated: 'function _dump' in generated, '-Os disables inlining'), + (['-s', 'USE_TYPED_ARRAYS=0'], lambda generated: 'new Int32Array' not in generated, 'disable typed arrays'), + (['-s', 'USE_TYPED_ARRAYS=1'], lambda generated: 'IHEAPU = ' in generated, 'typed arrays 1 selected'), + ([], lambda generated: 'Module["_dump"]' not in generated, 'dump is not exported by default'), + (['-s', 'EXPORTED_FUNCTIONS=["_main", "_dump"]'], lambda generated: 'Module["_dump"]' in generated, 'dump is now exported'), + (['--typed-arrays', '0'], lambda generated: 'new Int32Array' not in generated, 'disable typed arrays'), + (['--typed-arrays', '1'], lambda generated: 'IHEAPU = ' in generated, 'typed arrays 1 selected'), + (['--typed-arrays', '2'], lambda generated: 'new Uint16Array' in generated and 'new Uint32Array' in generated, 'typed arrays 2 selected'), + (['--llvm-opts', '1'], lambda generated: '_puts(' in generated, 'llvm opts requested'), + ]: + print params, text + self.clear() + output = Popen([PYTHON, compiler, path_from_root('tests', 'hello_world_loop.cpp'), '-o', 'a.out.js'] + params, stdout=PIPE, stderr=PIPE).communicate() + assert len(output[0]) == 0, output[0] + assert os.path.exists('a.out.js'), '\n'.join(output) + self.assertContained('hello, world!', run_js('a.out.js')) + assert test(open('a.out.js').read()), text + + # Compiling two source files into a final JS. + for args, target in [([], 'a.out.js'), (['-o', 'combined.js'], 'combined.js')]: + self.clear() + output = Popen([PYTHON, compiler, path_from_root('tests', 'twopart_main.cpp'), path_from_root('tests', 'twopart_side.cpp')] + args, + stdout=PIPE, stderr=PIPE).communicate() + assert len(output[0]) == 0, output[0] + assert os.path.exists(target), '\n'.join(output) + self.assertContained('side got: hello from main, over', run_js(target)) + + # Compiling two files with -c will generate separate .bc files + self.clear() + output = Popen([PYTHON, compiler, path_from_root('tests', 'twopart_main.cpp'), path_from_root('tests', 'twopart_side.cpp'), '-c'] + args, + stdout=PIPE, stderr=PIPE).communicate() + if '-o' in args: + # specifying -o and -c is an error + assert 'fatal error' in output[1], output[1] + continue + + assert os.path.exists('twopart_main.o'), '\n'.join(output) + assert os.path.exists('twopart_side.o'), '\n'.join(output) + assert not os.path.exists(target), 'We should only have created bitcode here: ' + '\n'.join(output) + + # Compiling one of them alone is expected to fail + output = Popen([PYTHON, compiler, 'twopart_main.o', '-O1', '-g'] + args, stdout=PIPE, stderr=PIPE).communicate() + assert os.path.exists(target), '\n'.join(output) + #print '\n'.join(output) + self.assertContained('missing function', run_js(target, stderr=STDOUT)) + try_delete(target) + + # Combining those bc files into js should work + output = Popen([PYTHON, compiler, 'twopart_main.o', 'twopart_side.o'] + args, stdout=PIPE, stderr=PIPE).communicate() + assert os.path.exists(target), '\n'.join(output) + self.assertContained('side got: hello from main, over', run_js(target)) + + # Combining bc files into another bc should also work + try_delete(target) + assert not os.path.exists(target) + output = Popen([PYTHON, compiler, 'twopart_main.o', 'twopart_side.o', '-o', 'combined.bc'] + args, stdout=PIPE, stderr=PIPE).communicate() + syms = Building.llvm_nm('combined.bc') + assert len(syms.defs) == 2 and 'main' in syms.defs, 'Failed to generate valid bitcode' + output = Popen([PYTHON, compiler, 'combined.bc', '-o', 'combined.bc.js'], stdout = PIPE, stderr = PIPE).communicate() + assert len(output[0]) == 0, output[0] + assert os.path.exists('combined.bc.js'), 'Expected %s to exist' % ('combined.bc.js') + self.assertContained('side got: hello from main, over', run_js('combined.bc.js')) + + # --js-transform + self.clear() + trans = os.path.join(self.get_dir(), 't.py') + trans_file = open(trans, 'w') + trans_file.write(''' +import sys +f = open(sys.argv[1], 'w') +f.write('transformed!') +f.close() +''') + trans_file.close() + output = Popen([PYTHON, compiler, path_from_root('tests', 'hello_world' + suffix), '--js-transform', '%s t.py' % (PYTHON)], stdout=PIPE, stderr=PIPE).communicate() + assert open('a.out.js').read() == 'transformed!', 'Transformed output must be as expected' + + # TODO: Add in files test a clear example of using disablePermissions, and link to it from the wiki + # TODO: test normal project linking, static and dynamic: get_library should not need to be told what to link! + # TODO: deprecate llvm optimizations, dlmalloc, etc. in emscripten.py. + + def test_cmake(self): + # On Windows, we want to build cmake-generated Makefiles with mingw32-make instead of e.g. cygwin make, since mingw32-make + # understands Windows paths, and cygwin make additionally produces a cryptic 'not valid bitcode file' errors on files that + # *are* valid bitcode files. + + if os.name == 'nt': + make_command = 'mingw32-make' + emscriptencmaketoolchain = path_from_root('cmake', 'Platform', 'Emscripten.cmake') + else: + make_command = 'make' + emscriptencmaketoolchain = path_from_root('cmake', 'Platform', 'Emscripten_unix.cmake') + + cmake_cases = ['target_js', 'target_html'] + cmake_outputs = ['hello_world.js', 'hello_world_gles.html'] + for i in range(0, 2): + for configuration in ['Debug', 'Release']: + + # Create a temp workspace folder + cmakelistsdir = path_from_root('tests', 'cmake', cmake_cases[i]) + tempdirname = tempfile.mkdtemp(prefix='emscripten_test_' + self.__class__.__name__ + '_', dir=TEMP_DIR) + try: + os.chdir(tempdirname) + + # Run Cmake + cmd = ['cmake', '-DCMAKE_TOOLCHAIN_FILE='+emscriptencmaketoolchain, + '-DCMAKE_BUILD_TYPE=' + configuration, + '-DCMAKE_MODULE_PATH=' + path_from_root('cmake').replace('\\', '/'), + '-G' 'Unix Makefiles', cmakelistsdir] + ret = Popen(cmd, stdout=PIPE, stderr=PIPE).communicate() + if ret[1] != None and len(ret[1].strip()) > 0: + print >> sys.stderr, ret[1] # If there were any errors, print them directly to console for diagnostics. + if 'error' in ret[1].lower(): + print >> sys.stderr, 'Failed command: ' + ' '.join(cmd) + print >> sys.stderr, 'Result:\n' + ret[1] + raise Exception('cmake call failed!') + assert os.path.exists(tempdirname + '/Makefile'), 'CMake call did not produce a Makefile!' + + # Build + cmd = [make_command] + ret = Popen(cmd, stdout=PIPE).communicate() + if ret[1] != None and len(ret[1].strip()) > 0: + print >> sys.stderr, ret[1] # If there were any errors, print them directly to console for diagnostics. + if 'error' in ret[0].lower() and not '0 error(s)' in ret[0].lower(): + print >> sys.stderr, 'Failed command: ' + ' '.join(cmd) + print >> sys.stderr, 'Result:\n' + ret[0] + raise Exception('make failed!') + assert os.path.exists(tempdirname + '/' + cmake_outputs[i]), 'Building a cmake-generated Makefile failed to produce an output file %s!' % tempdirname + '/' + cmake_outputs[i] + + # Run through node, if CMake produced a .js file. + if cmake_outputs[i].endswith('.js'): + ret = Popen(listify(NODE_JS) + [tempdirname + '/' + cmake_outputs[i]], stdout=PIPE).communicate()[0] + assert 'hello, world!' in ret, 'Running cmake-based .js application failed!' + finally: + os.chdir(path_from_root('tests')) # Move away from the directory we are about to remove. + shutil.rmtree(tempdirname) + + def test_nostdincxx(self): + try: + old = os.environ.get('EMCC_LLVM_TARGET') or '' + for compiler in [EMCC, EMXX]: + for target in ['i386-pc-linux-gnu', 'le32-unknown-nacl']: + print compiler, target + os.environ['EMCC_LLVM_TARGET'] = target + out, err = Popen([PYTHON, EMCC, path_from_root('tests', 'hello_world.cpp'), '-v'], stdout=PIPE, stderr=PIPE).communicate() + out2, err2 = Popen([PYTHON, EMCC, path_from_root('tests', 'hello_world.cpp'), '-v', '-nostdinc++'], stdout=PIPE, stderr=PIPE).communicate() + assert out == out2 + def focus(e): + assert 'search starts here:' in e, e + assert e.count('End of search list.') == 1, e + return e[e.index('search starts here:'):e.index('End of search list.')+20] + err = focus(err) + err2 = focus(err2) + assert err == err2, err + '\n\n\n\n' + err2 + finally: + if old: + os.environ['EMCC_LLVM_TARGET'] = old + + def test_failure_error_code(self): + for compiler in [EMCC, EMXX]: + # Test that if one file is missing from the build, then emcc shouldn't succeed, and shouldn't try to produce an output file. + process = Popen([PYTHON, compiler, path_from_root('tests', 'hello_world.c'), 'this_file_is_missing.c', '-o', 'this_output_file_should_never_exist.js'], stdout=PIPE, stderr=PIPE) + process.communicate() + assert process.returncode is not 0, 'Trying to compile a nonexisting file should return with a nonzero error code!' + assert os.path.exists('this_output_file_should_never_exist.js') == False, 'Emcc should not produce an output file when build fails!' + + def test_cxx03(self): + for compiler in [EMCC, EMXX]: + process = Popen([PYTHON, compiler, path_from_root('tests', 'hello_cxx03.cpp')], stdout=PIPE, stderr=PIPE) + process.communicate() + assert process.returncode is 0, 'By default, emscripten should build using -std=c++03!' + + def test_cxx11(self): + for compiler in [EMCC, EMXX]: + process = Popen([PYTHON, compiler, '-std=c++11', path_from_root('tests', 'hello_cxx11.cpp')], stdout=PIPE, stderr=PIPE) + process.communicate() + assert process.returncode is 0, 'User should be able to specify custom -std= on the command line!' + + def test_catch_undef(self): + open(os.path.join(self.get_dir(), 'test.cpp'), 'w').write(r''' + #include + #include + + class Test { + public: + std::vector vector; + }; + + Test globalInstance; + + int main() { + printf("hello, world!\n"); + return 0; + } + ''') + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'test.cpp'), '-fsanitize=undefined']).communicate() + self.assertContained('hello, world!', run_js(os.path.join(self.get_dir(), 'a.out.js'))) + + def test_unaligned_memory(self): + open(os.path.join(self.get_dir(), 'test.cpp'), 'w').write(r''' + #include + + typedef unsigned char Bit8u; + typedef unsigned short Bit16u; + typedef unsigned int Bit32u; + + int main() + { + Bit8u data[4] = {0x01,0x23,0x45,0x67}; + + printf("data: %x\n", *(Bit32u*)data); + printf("data[0,1] 16bit: %x\n", *(Bit16u*)data); + printf("data[1,2] 16bit: %x\n", *(Bit16u*)(data+1)); + } + ''') + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'test.cpp'), '-s', 'UNALIGNED_MEMORY=1']).communicate() + self.assertContained('data: 67452301\ndata[0,1] 16bit: 2301\ndata[1,2] 16bit: 4523', run_js(os.path.join(self.get_dir(), 'a.out.js'))) + + def test_unaligned_memory_2(self): + open(os.path.join(self.get_dir(), 'test.cpp'), 'w').write(r''' + #include + #include + + int main( int argc, char ** argv ) + { + std::string testString( "Hello, World!" ); + + printf( "testString = %s\n", testString.c_str() ); + return 0; + } + ''') + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'test.cpp'), '-s', 'UNALIGNED_MEMORY=1']).communicate() + self.assertContained('testString = Hello, World!', run_js(os.path.join(self.get_dir(), 'a.out.js'))) + + def test_asm_minify(self): + def test(args): + Popen([PYTHON, EMCC, path_from_root('tests', 'hello_world_loop_malloc.cpp')] + args).communicate() + self.assertContained('hello, world!', run_js(self.in_dir('a.out.js'))) + return open(self.in_dir('a.out.js')).read() + + src = test([]) + assert 'function _malloc' in src + + src = test(['-O2', '-s', 'ASM_JS=1']) + normal_size = len(src) + print 'normal', normal_size + assert 'function _malloc' not in src + + src = test(['-O2', '-s', 'ASM_JS=1', '--minify', '0']) + unminified_size = len(src) + print 'unminified', unminified_size + assert unminified_size > normal_size + assert 'function _malloc' not in src + + src = test(['-O2', '-s', 'ASM_JS=1', '-g']) + debug_size = len(src) + print 'debug', debug_size + assert debug_size > unminified_size + assert 'function _malloc' in src + + def test_dangerous_func_cast(self): + src = r''' + #include + typedef void (*voidfunc)(); + int my_func() { + printf("my func\n"); + return 10; + } + int main(int argc, char **argv) { + voidfunc fps[10]; + for (int i = 0; i < 10; i++) fps[i] = (i == argc) ? (void (*)())my_func : NULL; + fps[2*(argc-1) + 1](); + return 0; + } + ''' + open('src.c', 'w').write(src) + def test(args, expected, err_expected=None): + out, err = Popen([PYTHON, EMCC, 'src.c'] + args, stderr=PIPE).communicate() + if err_expected: self.assertContained(err_expected, err) + self.assertContained(expected, run_js(self.in_dir('a.out.js'), stderr=PIPE, full_output=True)) + return open(self.in_dir('a.out.js')).read() + + test([], 'my func') # no asm, so casting func works + test(['-O2'], 'abort', ['Casting potentially incompatible function pointer i32 ()* to void (...)*, for my_func', + 'Incompatible function pointer casts are very dangerous with ASM_JS=1, you should investigate and correct these']) # asm, so failure + test(['-O2', '-s', 'ASSERTIONS=1'], + 'Invalid function pointer called. Perhaps a miscast function pointer (check compilation warnings) or bad vtable lookup (maybe due to derefing a bad pointer, like NULL)?', + ['Casting potentially incompatible function pointer i32 ()* to void (...)*, for my_func', + 'Incompatible function pointer casts are very dangerous with ASM_JS=1, you should investigate and correct these']) # asm, so failure + + def test_l_link(self): + # Linking with -lLIBNAME and -L/DIRNAME should work + + open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write(''' + extern void printey(); + int main() { + printey(); + return 0; + } + ''') + + try: + os.makedirs(os.path.join(self.get_dir(), 'libdir')); + except: + pass + + open(os.path.join(self.get_dir(), 'libdir', 'libfile.cpp'), 'w').write(''' + #include + void printey() { + printf("hello from lib\\n"); + } + ''') + + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'libdir', 'libfile.cpp'), '-c']).communicate() + shutil.move(os.path.join(self.get_dir(), 'libfile.o'), os.path.join(self.get_dir(), 'libdir', 'libfile.so')) + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '-L' + os.path.join(self.get_dir(), 'libdir'), '-lfile']).communicate() + self.assertContained('hello from lib', run_js(os.path.join(self.get_dir(), 'a.out.js'))) + assert not os.path.exists('a.out') and not os.path.exists('a.exe'), 'Must not leave unneeded linker stubs' + + def test_static_link(self): + def test(name, header, main, side, expected, args=[], suffix='cpp', first=True): + print name + #t = main ; main = side ; side = t + original_main = main + original_side = side + if header: open(os.path.join(self.get_dir(), 'header.h'), 'w').write(header) + if type(main) == str: + open(os.path.join(self.get_dir(), 'main.' + suffix), 'w').write(main) + main = ['main.' + suffix] + if type(side) == str: + open(os.path.join(self.get_dir(), 'side.' + suffix), 'w').write(side) + side = ['side.' + suffix] + Popen([PYTHON, EMCC] + side + ['-o', 'side.js', '-s', 'SIDE_MODULE=1', '-O2'] + args).communicate() + # TODO: test with and without DISABLE_GL_EMULATION, check that file sizes change + Popen([PYTHON, EMCC] + main + ['-o', 'main.js', '-s', 'MAIN_MODULE=1', '-O2', '-s', 'DISABLE_GL_EMULATION=1'] + args).communicate() + Popen([PYTHON, EMLINK, 'main.js', 'side.js', 'together.js'], stdout=PIPE).communicate() + assert os.path.exists('together.js') + for engine in JS_ENGINES: + out = run_js('together.js', engine=engine, stderr=PIPE, full_output=True) + self.assertContained(expected, out) + if engine == SPIDERMONKEY_ENGINE: self.validate_asmjs(out) + if first: + shutil.copyfile('together.js', 'first.js') + test(name + ' (reverse)', header, original_side, original_main, expected, args, suffix, False) # test reverse order + + # test a simple call from one module to another. only one has a string (and constant memory initialization for it) + test('basics', '', ''' + #include + extern int sidey(); + int main() { + printf("other says %d.", sidey()); + return 0; + } + ''', ''' + int sidey() { return 11; } + ''', 'other says 11.') + + # finalization of float variables should pass asm.js validation + test('floats', '', ''' + #include + extern float sidey(); + int main() { + printf("other says %.2f.", sidey()+1); + return 0; + } + ''', ''' + float sidey() { return 11.5; } + ''', 'other says 12.50') + + # memory initialization in both + test('multiple memory inits', '', r''' + #include + extern void sidey(); + int main() { + printf("hello from main\n"); + sidey(); + return 0; + } + ''', r''' + #include + void sidey() { printf("hello from side\n"); } + ''', 'hello from main\nhello from side\n') + + # function pointers + test('fp1', 'typedef void (*voidfunc)();', r''' + #include + #include "header.h" + voidfunc sidey(voidfunc f); + void a() { printf("hello from funcptr\n"); } + int main() { + sidey(a)(); + return 0; + } + ''', ''' + #include "header.h" + voidfunc sidey(voidfunc f) { return f; } + ''', 'hello from funcptr\n') + + # function pointers with 'return' in the name + test('fp2', 'typedef void (*voidfunc)();', r''' + #include + #include "header.h" + int sidey(voidfunc f); + void areturn0() { printf("hello 0\n"); } + void areturn1() { printf("hello 1\n"); } + void areturn2() { printf("hello 2\n"); } + int main(int argc, char **argv) { + voidfunc table[3] = { areturn0, areturn1, areturn2 }; + table[sidey(NULL)](); + return 0; + } + ''', ''' + #include "header.h" + int sidey(voidfunc f) { if (f) f(); return 1; } + ''', 'hello 1\n') + + # Global initializer + test('global init', '', r''' + #include + struct Class { + Class() { printf("a new Class\n"); } + }; + static Class c; + int main() { + return 0; + } + ''', r''' + void nothing() {} + ''', 'a new Class\n') + + # Multiple global initializers (LLVM generates overlapping names for them) + test('global inits', r''' + #include + struct Class { + Class(const char *name) { printf("new %s\n", name); } + }; + ''', r''' + #include "header.h" + static Class c("main"); + int main() { + return 0; + } + ''', r''' + #include "header.h" + static Class c("side"); + ''', ['new main\nnew side\n', 'new side\nnew main\n']) + + # Class code used across modules + test('codecall', r''' + #include + struct Class { + Class(const char *name); + }; + ''', r''' + #include "header.h" + int main() { + Class c("main"); + return 0; + } + ''', r''' + #include "header.h" + Class::Class(const char *name) { printf("new %s\n", name); } + ''', ['new main\n']) + + # malloc usage in both modules + test('malloc', r''' + #include + #include + char *side(const char *data); + ''', r''' + #include + #include "header.h" + int main() { + char *temp = side("hello through side\n"); + char *ret = (char*)malloc(strlen(temp)+1); + strcpy(ret, temp); + temp[1] = 'x'; + puts(ret); + return 0; + } + ''', r''' + #include "header.h" + char *side(const char *data) { + char *ret = (char*)malloc(strlen(data)+1); + strcpy(ret, data); + return ret; + } + ''', ['hello through side\n']) + + # libc usage in one modules. must force libc inclusion in the main module if that isn't the one using mallinfo() + try: + os.environ['EMCC_FORCE_STDLIBS'] = 'libc' + test('malloc-1', r''' + #include + int side(); + ''', r''' + #include + #include "header.h" + int main() { + printf("|%d|\n", side()); + return 0; + } + ''', r''' + #include + #include + #include "header.h" + int side() { + struct mallinfo m = mallinfo(); + return m.arena > 1; + } + ''', ['|1|\n']) + finally: + del os.environ['EMCC_FORCE_STDLIBS'] + + # iostream usage in one and std::string in both + test('iostream', r''' + #include + #include + std::string side(); + ''', r''' + #include "header.h" + int main() { + std::cout << "hello from main " << side() << std::endl; + return 0; + } + ''', r''' + #include "header.h" + std::string side() { return "and hello from side"; } + ''', ['hello from main and hello from side\n']) + + # followup to iostream test: a second linking + print 'second linking of a linking output' + open('moar.cpp', 'w').write(r''' + #include + struct Moar { + Moar() { std::cout << "moar!" << std::endl; } + }; + Moar m; + ''') + Popen([PYTHON, EMCC, 'moar.cpp', '-o', 'moar.js', '-s', 'SIDE_MODULE=1', '-O2']).communicate() + Popen([PYTHON, EMLINK, 'together.js', 'moar.js', 'triple.js'], stdout=PIPE).communicate() + assert os.path.exists('triple.js') + for engine in JS_ENGINES: + out = run_js('triple.js', engine=engine, stderr=PIPE, full_output=True) + self.assertContained('moar!\nhello from main and hello from side\n', out) + if engine == SPIDERMONKEY_ENGINE: self.validate_asmjs(out) + + # zlib compression library. tests function pointers in initializers and many other things + test('zlib', '', open(path_from_root('tests', 'zlib', 'example.c'), 'r').read(), + self.get_library('zlib', os.path.join('libz.a'), make_args=['libz.a']), + open(path_from_root('tests', 'zlib', 'ref.txt'), 'r').read(), + args=['-I' + path_from_root('tests', 'zlib')], suffix='c') + + # bullet physics engine. tests all the things + test('bullet', '', open(path_from_root('tests', 'bullet', 'Demos', 'HelloWorld', 'HelloWorld.cpp'), 'r').read(), + self.get_library('bullet', [os.path.join('src', '.libs', 'libBulletDynamics.a'), + os.path.join('src', '.libs', 'libBulletCollision.a'), + os.path.join('src', '.libs', 'libLinearMath.a')]), + [open(path_from_root('tests', 'bullet', 'output.txt'), 'r').read(), # different roundings + open(path_from_root('tests', 'bullet', 'output2.txt'), 'r').read(), + open(path_from_root('tests', 'bullet', 'output3.txt'), 'r').read()], + args=['-I' + path_from_root('tests', 'bullet', 'src')]) + + + def test_outline(self): + def test(name, src, libs, expected, expected_ranges, args=[], suffix='cpp'): + print name + + def measure_funcs(filename): + i = 0 + start = -1 + curr = None + ret = {} + for line in open(filename): + i += 1 + if line.startswith('function '): + start = i + curr = line + elif line.startswith('}') and curr: + size = i - start + ret[curr] = size + curr = None + return ret + + for debug, outlining_limits in [ + ([], (1000,)), + (['-g1'], (1000,)), + (['-g2'], (1000,)), + (['-g'], (100, 250, 500, 1000, 2000, 5000, 0)) + ]: + for outlining_limit in outlining_limits: + print '\n', Building.COMPILER_TEST_OPTS, debug, outlining_limit, '\n' + # TODO: test without -g3, tell all sorts + Popen([PYTHON, EMCC, src] + libs + ['-o', 'test.js', '-O2'] + debug + ['-s', 'OUTLINING_LIMIT=%d' % outlining_limit] + args).communicate() + assert os.path.exists('test.js') + shutil.copyfile('test.js', '%d_test.js' % outlining_limit) + for engine in JS_ENGINES: + out = run_js('test.js', engine=engine, stderr=PIPE, full_output=True) + self.assertContained(expected, out) + if engine == SPIDERMONKEY_ENGINE: self.validate_asmjs(out) + if debug == ['-g']: + low = expected_ranges[outlining_limit][0] + seen = max(measure_funcs('test.js').values()) + high = expected_ranges[outlining_limit][1] + print Building.COMPILER_TEST_OPTS, outlining_limit, ' ', low, '<=', seen, '<=', high + assert low <= seen <= high + + for test_opts, expected_ranges in [ + ([], { + 100: (190, 250), + 250: (200, 330), + 500: (250, 500), + 1000: (230, 1000), + 2000: (380, 2000), + 5000: (800, 5000), + 0: (1500, 5000) + }), + (['-O2'], { + 100: (0, 1500), + 250: (0, 1500), + 500: (0, 1500), + 1000: (0, 1500), + 2000: (0, 2000), + 5000: (0, 5000), + 0: (0, 5000) + }), + ]: + Building.COMPILER_TEST_OPTS = test_opts + test('zlib', path_from_root('tests', 'zlib', 'example.c'), + self.get_library('zlib', os.path.join('libz.a'), make_args=['libz.a']), + open(path_from_root('tests', 'zlib', 'ref.txt'), 'r').read(), + expected_ranges, + args=['-I' + path_from_root('tests', 'zlib')], suffix='c') + + def test_symlink(self): + if os.name == 'nt': + return self.skip('Windows FS does not need to be tested for symlinks support, since it does not have them.') + open(os.path.join(self.get_dir(), 'foobar.xxx'), 'w').write('int main(){ return 0; }') + os.symlink(os.path.join(self.get_dir(), 'foobar.xxx'), os.path.join(self.get_dir(), 'foobar.c')) + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'foobar.c'), '-o', os.path.join(self.get_dir(), 'foobar')], stdout=PIPE, stderr=PIPE).communicate() + assert os.path.exists(os.path.join(self.get_dir(), 'foobar')) + try_delete(os.path.join(self.get_dir(), 'foobar')) + try_delete(os.path.join(self.get_dir(), 'foobar.xxx')) + try_delete(os.path.join(self.get_dir(), 'foobar.c')) + + open(os.path.join(self.get_dir(), 'foobar.c'), 'w').write('int main(){ return 0; }') + os.symlink(os.path.join(self.get_dir(), 'foobar.c'), os.path.join(self.get_dir(), 'foobar.xxx')) + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'foobar.xxx'), '-o', os.path.join(self.get_dir(), 'foobar')], stdout=PIPE, stderr=PIPE).communicate() + assert os.path.exists(os.path.join(self.get_dir(), 'foobar')) + try_delete(os.path.join(self.get_dir(), 'foobar')) + try_delete(os.path.join(self.get_dir(), 'foobar.xxx')) + try_delete(os.path.join(self.get_dir(), 'foobar.c')) + + def test_multiply_defined_libsymbols(self): + lib = "int mult() { return 1; }" + lib_name = os.path.join(self.get_dir(), 'libA.c') + open(lib_name, 'w').write(lib) + a2 = "void x() {}" + a2_name = os.path.join(self.get_dir(), 'a2.c') + open(a2_name, 'w').write(a2) + b2 = "void y() {}" + b2_name = os.path.join(self.get_dir(), 'b2.c') + open(b2_name, 'w').write(b2) + main = r''' + #include + int mult(); + int main() { + printf("result: %d\n", mult()); + return 0; + } + ''' + main_name = os.path.join(self.get_dir(), 'main.c') + open(main_name, 'w').write(main) + + Building.emcc(lib_name, output_filename='libA.so') + + Building.emcc(a2_name, ['-L.', '-lA']) + Building.emcc(b2_name, ['-L.', '-lA']) + + Building.emcc(main_name, ['-L.', '-lA', a2_name+'.o', b2_name+'.o'], output_filename='a.out.js') + + self.assertContained('result: 1', run_js(os.path.join(self.get_dir(), 'a.out.js'))) + + def test_multiply_defined_libsymbols_2(self): + a = "int x() { return 55; }" + a_name = os.path.join(self.get_dir(), 'a.c') + open(a_name, 'w').write(a) + b = "int y() { return 2; }" + b_name = os.path.join(self.get_dir(), 'b.c') + open(b_name, 'w').write(b) + c = "int z() { return 5; }" + c_name = os.path.join(self.get_dir(), 'c.c') + open(c_name, 'w').write(c) + main = r''' + #include + int x(); + int y(); + int z(); + int main() { + printf("result: %d\n", x() + y() + z()); + return 0; + } + ''' + main_name = os.path.join(self.get_dir(), 'main.c') + open(main_name, 'w').write(main) + + Building.emcc(a_name) # a.c.o + Building.emcc(b_name) # b.c.o + Building.emcc(c_name) # c.c.o + lib_name = os.path.join(self.get_dir(), 'libLIB.a') + Building.emar('cr', lib_name, [a_name + '.o', b_name + '.o']) # libLIB.a with a and b + + # a is in the lib AND in an .o, so should be ignored in the lib. We do still need b from the lib though + Building.emcc(main_name, ['-L.', '-lLIB', a_name+'.o', c_name + '.o'], output_filename='a.out.js') + + self.assertContained('result: 62', run_js(os.path.join(self.get_dir(), 'a.out.js'))) + + def test_redundant_link(self): + lib = "int mult() { return 1; }" + lib_name = os.path.join(self.get_dir(), 'libA.c') + open(lib_name, 'w').write(lib) + main = r''' + #include + int mult(); + int main() { + printf("result: %d\n", mult()); + return 0; + } + ''' + main_name = os.path.join(self.get_dir(), 'main.c') + open(main_name, 'w').write(main) + + Building.emcc(lib_name, output_filename='libA.so') + + Building.emcc(main_name, ['libA.so']*2, output_filename='a.out.js') + + self.assertContained('result: 1', run_js(os.path.join(self.get_dir(), 'a.out.js'))) + + def test_export_all(self): + lib = r''' + #include + void libf1() { printf("libf1\n"); } + void libf2() { printf("libf2\n"); } + ''' + lib_name = os.path.join(self.get_dir(), 'lib.c') + open(lib_name, 'w').write(lib) + + open('main.js', 'w').write(''' + _libf1(); + _libf2(); + ''') + + Building.emcc(lib_name, ['-s', 'EXPORT_ALL=1', '--post-js', 'main.js'], output_filename='a.out.js') + + self.assertContained('libf1\nlibf2\n', run_js(os.path.join(self.get_dir(), 'a.out.js'))) + + def test_stdin(self): + open('main.cpp', 'w').write(r''' +#include +int main(int argc, char const *argv[]) +{ + char str[10] = {0}; + scanf("%10s", str); + printf("%s\n", str); + return 0; +} +''') + Building.emcc('main.cpp', output_filename='a.out.js') + open('in.txt', 'w').write('abc') + # node's stdin support is broken + self.assertContained('abc', Popen(listify(SPIDERMONKEY_ENGINE) + ['a.out.js'], stdin=open('in.txt'), stdout=PIPE, stderr=PIPE).communicate()[0]) + + def test_ungetc_fscanf(self): + open('main.cpp', 'w').write(r''' + #include + int main(int argc, char const *argv[]) + { + char str[4] = {0}; + FILE* f = fopen("my_test.input", "r"); + if (f == NULL) { + printf("cannot open file\n"); + return -1; + } + ungetc('x', f); + ungetc('y', f); + ungetc('z', f); + fscanf(f, "%3s", str); + printf("%s\n", str); + return 0; + } + ''') + open('my_test.input', 'w').write('abc') + Building.emcc('main.cpp', ['--embed-file', 'my_test.input'], output_filename='a.out.js') + self.assertContained('zyx', Popen(listify(JS_ENGINES[0]) + ['a.out.js'], stdout=PIPE, stderr=PIPE).communicate()[0]) + + def test_abspaths(self): + # Includes with absolute paths are generally dangerous, things like -I/usr/.. will get to system local headers, not our portable ones. + + shutil.copyfile(path_from_root('tests', 'hello_world.c'), 'main.c') + + for args, expected in [(['-I/usr/something'], True), + (['-L/usr/something'], True), + (['-I/usr/something', '-Wno-warn-absolute-paths'], False), + (['-L/usr/something', '-Wno-warn-absolute-paths'], False), + (['-Isubdir/something'], False), + (['-Lsubdir/something'], False), + ([], False)]: + err = Popen([PYTHON, EMCC, 'main.c'] + args, stderr=PIPE).communicate()[1] + assert ('encountered. If this is to a local system header/library, it may cause problems (local system files make sense for compiling natively on your system, but not necessarily to JavaScript)' in err) == expected, err + + def test_local_link(self): + # Linking a local library directly, like /usr/lib/libsomething.so, cannot work of course since it + # doesn't contain bitcode. However, when we see that we should look for a bitcode file for that + # library in the -L paths and system/lib + open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write(''' + extern void printey(); + int main() { + printey(); + return 0; + } + ''') + + try: + os.makedirs(os.path.join(self.get_dir(), 'subdir')); + except: + pass + open(os.path.join(self.get_dir(), 'subdir', 'libfile.so'), 'w').write('this is not llvm bitcode!') + + open(os.path.join(self.get_dir(), 'libfile.cpp'), 'w').write(''' + #include + void printey() { + printf("hello from lib\\n"); + } + ''') + + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'libfile.cpp'), '-o', 'libfile.so']).communicate() + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), os.path.join(self.get_dir(), 'subdir', 'libfile.so'), '-L.'], stderr=PIPE).communicate() + self.assertContained('hello from lib', run_js(os.path.join(self.get_dir(), 'a.out.js'))) + + def test_runtimelink_multi(self): + return self.skip('shared libs are deprecated') + if Settings.ASM_JS: return self.skip('asm does not support runtime linking yet') + + if SPIDERMONKEY_ENGINE not in JS_ENGINES: return self.skip('cannot run without spidermonkey due to node limitations') + + open('testa.h', 'w').write(r''' + #ifndef _TESTA_H_ + #define _TESTA_H_ + + class TestA { + public: + TestA(); + }; + + #endif + ''') + open('testb.h', 'w').write(r''' + #ifndef _TESTB_H_ + #define _TESTB_H_ + + class TestB { + public: + TestB(); + }; + + #endif + ''') + open('testa.cpp', 'w').write(r''' + #include + #include + + TestA::TestA() { + printf("TestA\n"); + } + ''') + open('testb.cpp', 'w').write(r''' + #include + #include + #include + /* + */ + TestB::TestB() { + printf("TestB\n"); + TestA* testa = new TestA(); + } + ''') + open('main.cpp', 'w').write(r''' + #include + #include + #include + + /* + */ + int main(int argc, char** argv) { + printf("Main\n"); + TestA* testa = new TestA(); + TestB* testb = new TestB(); + } + ''') + + Popen([PYTHON, EMCC, 'testa.cpp', '-o', 'liba.js', '-s', 'BUILD_AS_SHARED_LIB=2', '-s', 'LINKABLE=1', '-s', 'NAMED_GLOBALS=1', '-I.']).communicate() + Popen([PYTHON, EMCC, 'testb.cpp', '-o', 'libb.js', '-s', 'BUILD_AS_SHARED_LIB=2', '-s', 'LINKABLE=1', '-s', 'NAMED_GLOBALS=1', '-I.']).communicate() + Popen([PYTHON, EMCC, 'main.cpp', '-o', 'main.js', '-s', 'RUNTIME_LINKED_LIBS=["liba.js", "libb.js"]', '-s', 'NAMED_GLOBALS=1', '-I.', '-s', 'LINKABLE=1']).communicate() + + Popen([PYTHON, EMCC, 'main.cpp', 'testa.cpp', 'testb.cpp', '-o', 'full.js', '-I.']).communicate() + + self.assertContained('TestA\nTestB\nTestA\n', run_js('main.js', engine=SPIDERMONKEY_ENGINE)) + + def test_js_libraries(self): + open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write(''' + #include + extern "C" { + extern void printey(); + extern int calcey(int x, int y); + } + int main() { + printey(); + printf("*%d*\\n", calcey(10, 22)); + return 0; + } + ''') + open(os.path.join(self.get_dir(), 'mylib1.js'), 'w').write(''' + mergeInto(LibraryManager.library, { + printey: function() { + Module.print('hello from lib!'); + } + }); + ''') + open(os.path.join(self.get_dir(), 'mylib2.js'), 'w').write(''' + mergeInto(LibraryManager.library, { + calcey: function(x, y) { + return x + y; + } + }); + ''') + + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--js-library', os.path.join(self.get_dir(), 'mylib1.js'), + '--js-library', os.path.join(self.get_dir(), 'mylib2.js')]).communicate() + self.assertContained('hello from lib!\n*32*\n', run_js(os.path.join(self.get_dir(), 'a.out.js'))) + + def test_identical_basenames(self): + # Issue 287: files in different dirs but with the same basename get confused as the same, + # causing multiply defined symbol errors + try: + os.makedirs(os.path.join(self.get_dir(), 'foo')); + except: + pass + try: + os.makedirs(os.path.join(self.get_dir(), 'bar')); + except: + pass + open(os.path.join(self.get_dir(), 'foo', 'main.cpp'), 'w').write(''' + extern void printey(); + int main() { + printey(); + return 0; + } + ''') + open(os.path.join(self.get_dir(), 'bar', 'main.cpp'), 'w').write(''' + #include + void printey() { printf("hello there\\n"); } + ''') + + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'foo', 'main.cpp'), os.path.join(self.get_dir(), 'bar', 'main.cpp')]).communicate() + self.assertContained('hello there', run_js(os.path.join(self.get_dir(), 'a.out.js'))) + + # ditto with first creating .o files + try_delete(os.path.join(self.get_dir(), 'a.out.js')) + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'foo', 'main.cpp'), '-o', os.path.join(self.get_dir(), 'foo', 'main.o')]).communicate() + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'bar', 'main.cpp'), '-o', os.path.join(self.get_dir(), 'bar', 'main.o')]).communicate() + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'foo', 'main.o'), os.path.join(self.get_dir(), 'bar', 'main.o')]).communicate() + self.assertContained('hello there', run_js(os.path.join(self.get_dir(), 'a.out.js'))) + + def test_main_a(self): + # if main() is in a .a, we need to pull in that .a + + main_name = os.path.join(self.get_dir(), 'main.c') + open(main_name, 'w').write(r''' + #include + extern int f(); + int main() { + printf("result: %d.\n", f()); + return 0; + } + ''') + + other_name = os.path.join(self.get_dir(), 'other.c') + open(other_name, 'w').write(r''' + #include + int f() { return 12346; } + ''') + + Popen([PYTHON, EMCC, main_name, '-c', '-o', main_name+'.bc']).communicate() + Popen([PYTHON, EMCC, other_name, '-c', '-o', other_name+'.bc']).communicate() + + Popen([PYTHON, EMAR, 'cr', main_name+'.a', main_name+'.bc']).communicate() + + Popen([PYTHON, EMCC, other_name+'.bc', main_name+'.a']).communicate() + + self.assertContained('result: 12346.', run_js(os.path.join(self.get_dir(), 'a.out.js'))) + + def test_dup_o_in_a(self): + open('common.c', 'w').write(r''' + #include + void a(void) { + printf("a\n"); + } + ''') + Popen([PYTHON, EMCC, 'common.c', '-c', '-o', 'common.o']).communicate() + Popen([PYTHON, EMAR, 'rc', 'liba.a', 'common.o']).communicate() + + open('common.c', 'w').write(r''' + #include + void b(void) { + printf("b\n"); + } + ''') + Popen([PYTHON, EMCC, 'common.c', '-c', '-o', 'common.o']).communicate() + Popen([PYTHON, EMAR, 'rc', 'libb.a', 'common.o']).communicate() + + open('main.c', 'w').write(r''' + void a(void); + void b(void); + int main() { + a(); + b(); + } + ''') + Popen([PYTHON, EMCC, 'main.c', '-L.', '-la', '-lb']).communicate() + + self.assertContained('a\nb\n', run_js(os.path.join(self.get_dir(), 'a.out.js'))) + + def test_export_in_a(self): + export_name = 'this_is_an_entry_point' + + open('export.c', 'w').write(r''' + #include + void %s(void) { + printf("Hello, world!\n"); + } + ''' % export_name) + Popen([PYTHON, EMCC, 'export.c', '-c', '-o', 'export.o']).communicate() + Popen([PYTHON, EMAR, 'rc', 'libexport.a', 'export.o']).communicate() + + open('main.c', 'w').write(r''' + int main() { + return 0; + } + ''') + + definition = 'function _%s(' % export_name + + # Sanity check: the symbol should not be linked in if not requested. + Popen([PYTHON, EMCC, 'main.c', '-L.', '-lexport']).communicate() + self.assertNotContained(definition, open(os.path.join(self.get_dir(), 'a.out.js')).read()) + + # Sanity check: exporting without a definition does not cause it to appear. + # Note: exporting main prevents emcc from warning that it generated no code. + Popen([PYTHON, EMCC, 'main.c', '-s', '''EXPORTED_FUNCTIONS=['_main', '_%s']''' % export_name]).communicate() + self.assertNotContained(definition, open(os.path.join(self.get_dir(), 'a.out.js')).read()) + + # Actual test: defining symbol in library and exporting it causes it to appear in the output. + Popen([PYTHON, EMCC, 'main.c', '-L.', '-lexport', '-s', '''EXPORTED_FUNCTIONS=['_%s']''' % export_name]).communicate() + self.assertContained(definition, open(os.path.join(self.get_dir(), 'a.out.js')).read()) + + def test_embed_file(self): + open(os.path.join(self.get_dir(), 'somefile.txt'), 'w').write('''hello from a file with lots of data and stuff in it thank you very much''') + open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write(r''' + #include + int main() { + FILE *f = fopen("somefile.txt", "r"); + char buf[100]; + fread(buf, 1, 20, f); + buf[20] = 0; + fclose(f); + printf("|%s|\n", buf); + return 0; + } + ''') + + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--embed-file', 'somefile.txt']).communicate() + self.assertContained('|hello from a file wi|', run_js(os.path.join(self.get_dir(), 'a.out.js'))) + + # preload twice, should not err + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--embed-file', 'somefile.txt', '--embed-file', 'somefile.txt']).communicate() + self.assertContained('|hello from a file wi|', run_js(os.path.join(self.get_dir(), 'a.out.js'))) + + def test_embed_file_dup(self): + try_delete(os.path.join(self.get_dir(), 'tst')) + os.mkdir(os.path.join(self.get_dir(), 'tst')) + os.mkdir(os.path.join(self.get_dir(), 'tst', 'test1')) + os.mkdir(os.path.join(self.get_dir(), 'tst', 'test2')) + + open(os.path.join(self.get_dir(), 'tst', 'aa.txt'), 'w').write('''frist''') + open(os.path.join(self.get_dir(), 'tst', 'test1', 'aa.txt'), 'w').write('''sacond''') + open(os.path.join(self.get_dir(), 'tst', 'test2', 'aa.txt'), 'w').write('''thard''') + open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write(r''' + #include + #include + void print_file(const char *name) { + FILE *f = fopen(name, "r"); + char buf[100]; + memset(buf, 0, 100); + fread(buf, 1, 20, f); + buf[20] = 0; + fclose(f); + printf("|%s|\n", buf); + } + int main() { + print_file("tst/aa.txt"); + print_file("tst/test1/aa.txt"); + print_file("tst/test2/aa.txt"); + return 0; + } + ''') + + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--embed-file', 'tst']).communicate() + self.assertContained('|frist|\n|sacond|\n|thard|\n', run_js(os.path.join(self.get_dir(), 'a.out.js'))) + + def test_multidynamic_link(self): + # Linking the same dynamic library in will error, normally, since we statically link it, causing dupe symbols + # A workaround is to use --ignore-dynamic-linking, see emcc --help for details + + open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write(r''' + #include + extern void printey(); + extern void printother(); + int main() { + printf("*"); + printey(); + printf("\n"); + printother(); + printf("\n"); + printf("*"); + return 0; + } + ''') + + try: + os.makedirs(os.path.join(self.get_dir(), 'libdir')); + except: + pass + + open(os.path.join(self.get_dir(), 'libdir', 'libfile.cpp'), 'w').write(''' + #include + void printey() { + printf("hello from lib"); + } + ''') + + open(os.path.join(self.get_dir(), 'libdir', 'libother.cpp'), 'w').write(''' + #include + extern void printey(); + void printother() { + printf("|"); + printey(); + printf("|"); + } + ''') + + # This lets us link the same dynamic lib twice. We will need to link it in manually at the end. + compiler = [PYTHON, EMCC, '--ignore-dynamic-linking'] + + # Build libfile normally into an .so + Popen(compiler + [os.path.join(self.get_dir(), 'libdir', 'libfile.cpp'), '-o', os.path.join(self.get_dir(), 'libdir', 'libfile.so')]).communicate() + # Build libother and dynamically link it to libfile - but add --ignore-dynamic-linking + Popen(compiler + [os.path.join(self.get_dir(), 'libdir', 'libother.cpp'), '-L' + os.path.join(self.get_dir(), 'libdir'), '-lfile', '-o', os.path.join(self.get_dir(), 'libdir', 'libother.so')]).communicate() + # Build the main file, linking in both the libs + Popen(compiler + [os.path.join(self.get_dir(), 'main.cpp'), '-L' + os.path.join(self.get_dir(), 'libdir'), '-lfile', '-lother', '-c']).communicate() + + # The normal build system is over. We need to do an additional step to link in the dynamic libraries, since we ignored them before + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.o'), '-L' + os.path.join(self.get_dir(), 'libdir'), '-lfile', '-lother']).communicate() + + self.assertContained('*hello from lib\n|hello from lib|\n*', run_js(os.path.join(self.get_dir(), 'a.out.js'))) + + def test_js_link(self): + open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write(''' + #include + int main() { + printf("hello from main\\n"); + return 0; + } + ''') + open(os.path.join(self.get_dir(), 'before.js'), 'w').write(''' + var MESSAGE = 'hello from js'; + if (typeof Module != 'undefined') throw 'This code should run before anything else!'; + ''') + open(os.path.join(self.get_dir(), 'after.js'), 'w').write(''' + Module.print(MESSAGE); + ''') + + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--pre-js', 'before.js', '--post-js', 'after.js']).communicate() + self.assertContained('hello from main\nhello from js\n', run_js(os.path.join(self.get_dir(), 'a.out.js'))) + + def test_sdl_endianness(self): + open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write(r''' + #include + #include + + int main() { + printf("%d, %d, %d\n", SDL_BYTEORDER, SDL_LIL_ENDIAN, SDL_BIG_ENDIAN); + return 0; + } + ''') + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp')]).communicate() + self.assertContained('1234, 1234, 4321\n', run_js(os.path.join(self.get_dir(), 'a.out.js'))) + + def test_link_memcpy(self): + # memcpy can show up *after* optimizations, so after our opportunity to link in libc, so it must be special-cased + open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write(r''' + #include + + int main(int argc, char **argv) { + int num = argc + 10; + char buf[num], buf2[num]; + for (int i = 0; i < num; i++) { + buf[i] = i*i+i/3; + } + for (int i = 1; i < num; i++) { + buf[i] += buf[i-1]; + } + for (int i = 0; i < num; i++) { + buf2[i] = buf[i]; + } + for (int i = 1; i < num; i++) { + buf2[i] += buf2[i-1]; + } + for (int i = 0; i < num; i++) { + printf("%d:%d\n", i, buf2[i]); + } + return 0; + } + ''') + Popen([PYTHON, EMCC, '-O2', os.path.join(self.get_dir(), 'main.cpp')]).communicate() + output = run_js(os.path.join(self.get_dir(), 'a.out.js'), full_output=True, stderr=PIPE) + self.assertContained('''0:0 +1:1 +2:6 +3:21 +4:53 +5:111 +6:-49 +7:98 +8:55 +9:96 +10:-16 +''', output) + self.assertNotContained('warning: library.js memcpy should not be running, it is only for testing!', output) + + def test_warn_undefined(self): + open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write(r''' + #include + + extern "C" { + void something(); + } + + int main() { + something(); + return 0; + } + ''') + + def clear(): try_delete('a.out.js') + + for args in [[], ['-O2']]: + clear() + print 'warn', args + output = Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '-s', 'WARN_ON_UNDEFINED_SYMBOLS=1'] + args, stderr=PIPE).communicate() + self.assertContained('unresolved symbol: something', output[1]) + + clear() + output = Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp')] + args, stderr=PIPE).communicate() + self.assertNotContained('unresolved symbol: something\n', output[1]) + + for args in [[], ['-O2']]: + clear() + print 'error', args + output = Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '-s', 'ERROR_ON_UNDEFINED_SYMBOLS=1'] + args, stderr=PIPE).communicate() + self.assertContained('unresolved symbol: something', output[1]) + assert not os.path.exists('a.out.js') + + clear() + output = Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp')] + args, stderr=PIPE).communicate() + self.assertNotContained('unresolved symbol: something\n', output[1]) + assert os.path.exists('a.out.js') + + def test_toobig(self): + open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write(r''' + #include + + #define BYTES 100*1024*1024 + + int main(int argc, char **argv) { + if (argc == 100) { + static char buf[BYTES]; + static char buf2[BYTES]; + for (int i = 0; i < BYTES; i++) { + buf[i] = i*i; + buf2[i] = i/3; + } + for (int i = 0; i < BYTES; i++) { + buf[i] = buf2[i/2]; + buf2[i] = buf[i/3]; + } + printf("%d\n", buf[10] + buf2[20]); + } + return 0; + } + ''') + output = Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp')], stderr=PIPE).communicate()[1] + assert 'Emscripten failed' in output, output + assert 'warning: very large fixed-size structural type' in output, output + + def test_prepost(self): + open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write(''' + #include + int main() { + printf("hello from main\\n"); + return 0; + } + ''') + open(os.path.join(self.get_dir(), 'pre.js'), 'w').write(''' + var Module = { + preRun: function() { Module.print('pre-run') }, + postRun: function() { Module.print('post-run') } + }; + ''') + + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--pre-js', 'pre.js']).communicate() + self.assertContained('pre-run\nhello from main\npost-run\n', run_js(os.path.join(self.get_dir(), 'a.out.js'))) + + # never run, so no preRun or postRun + src = open(os.path.join(self.get_dir(), 'a.out.js')).read().replace('// {{PRE_RUN_ADDITIONS}}', 'addRunDependency()') + open(os.path.join(self.get_dir(), 'a.out.js'), 'w').write(src) + self.assertNotContained('pre-run\nhello from main\npost-run\n', run_js(os.path.join(self.get_dir(), 'a.out.js'))) + + # noInitialRun prevents run + for no_initial_run, run_dep in [(0, 0), (1, 0), (0, 1)]: + print no_initial_run, run_dep + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp')]).communicate() + src = 'var Module = { noInitialRun: %d };\n' % no_initial_run + open(os.path.join(self.get_dir(), 'a.out.js')).read() + if run_dep: + src = src.replace('// {{PRE_RUN_ADDITIONS}}', '// {{PRE_RUN_ADDITIONS}}\naddRunDependency("test");') \ + .replace('// {{POST_RUN_ADDITIONS}}', '// {{POST_RUN_ADDITIONS}}\nremoveRunDependency("test");') + open(os.path.join(self.get_dir(), 'a.out.js'), 'w').write(src) + assert ('hello from main' in run_js(os.path.join(self.get_dir(), 'a.out.js'))) != no_initial_run, 'only run if no noInitialRun' + + if no_initial_run: + # Calling main later should still work, filesystem etc. must be set up. + print 'call main later' + src = open(os.path.join(self.get_dir(), 'a.out.js')).read() + '\nModule.callMain();\n'; + open(os.path.join(self.get_dir(), 'a.out.js'), 'w').write(src) + assert 'hello from main' in run_js(os.path.join(self.get_dir(), 'a.out.js')), 'main should print when called manually' + + # Use postInit + open(os.path.join(self.get_dir(), 'pre.js'), 'w').write(''' + var Module = { + preRun: function() { Module.print('pre-run') }, + postRun: function() { Module.print('post-run') }, + preInit: function() { Module.print('pre-init') } + }; + ''') + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--pre-js', 'pre.js']).communicate() + self.assertContained('pre-init\npre-run\nhello from main\npost-run\n', run_js(os.path.join(self.get_dir(), 'a.out.js'))) + + def test_prepost2(self): + open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write(''' + #include + int main() { + printf("hello from main\\n"); + return 0; + } + ''') + open(os.path.join(self.get_dir(), 'pre.js'), 'w').write(''' + var Module = { + preRun: function() { Module.print('pre-run') }, + }; + ''') + open(os.path.join(self.get_dir(), 'pre2.js'), 'w').write(''' + Module.postRun = function() { Module.print('post-run') }; + ''') + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--pre-js', 'pre.js', '--pre-js', 'pre2.js']).communicate() + self.assertContained('pre-run\nhello from main\npost-run\n', run_js(os.path.join(self.get_dir(), 'a.out.js'))) + + def test_prepre(self): + open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write(''' + #include + int main() { + printf("hello from main\\n"); + return 0; + } + ''') + open(os.path.join(self.get_dir(), 'pre.js'), 'w').write(''' + var Module = { + preRun: [function() { Module.print('pre-run') }], + }; + ''') + open(os.path.join(self.get_dir(), 'pre2.js'), 'w').write(''' + Module.preRun.push(function() { Module.print('prepre') }); + ''') + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--pre-js', 'pre.js', '--pre-js', 'pre2.js']).communicate() + self.assertContained('prepre\npre-run\nhello from main\n', run_js(os.path.join(self.get_dir(), 'a.out.js'))) + + def test_save_bc(self): + for save in [0, 1]: + self.clear() + Popen([PYTHON, EMCC, path_from_root('tests', 'hello_world_loop_malloc.cpp')] + ([] if not save else ['--save-bc', self.in_dir('my_bitcode.bc')])).communicate() + assert 'hello, world!' in run_js(self.in_dir('a.out.js')) + assert os.path.exists(self.in_dir('my_bitcode.bc')) == save + if save: + try_delete('a.out.js') + Building.llvm_dis(self.in_dir('my_bitcode.bc'), self.in_dir('my_ll.ll')) + try: + os.environ['EMCC_LEAVE_INPUTS_RAW'] = '1' + Popen([PYTHON, EMCC, 'my_ll.ll', '-o', 'two.js']).communicate() + assert 'hello, world!' in run_js(self.in_dir('two.js')) + finally: + del os.environ['EMCC_LEAVE_INPUTS_RAW'] + + 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 [ + (path_from_root('tools', 'test-js-optimizer.js'), open(path_from_root('tools', 'test-js-optimizer-output.js')).read(), + ['hoistMultiples', 'loopOptimizer', 'removeAssignsToUndefined', 'simplifyExpressions']), + (path_from_root('tools', 'test-js-optimizer-t2c.js'), open(path_from_root('tools', 'test-js-optimizer-t2c-output.js')).read(), + ['simplifyExpressions', 'optimizeShiftsConservative']), + (path_from_root('tools', 'test-js-optimizer-t2.js'), open(path_from_root('tools', 'test-js-optimizer-t2-output.js')).read(), + ['simplifyExpressions', 'optimizeShiftsAggressive']), + # Make sure that optimizeShifts handles functions with shift statements. + (path_from_root('tools', 'test-js-optimizer-t3.js'), open(path_from_root('tools', 'test-js-optimizer-t3-output.js')).read(), + ['optimizeShiftsAggressive']), + (path_from_root('tools', 'test-js-optimizer-regs.js'), open(path_from_root('tools', 'test-js-optimizer-regs-output.js')).read(), + ['registerize']), + (path_from_root('tools', 'eliminator', 'eliminator-test.js'), open(path_from_root('tools', 'eliminator', 'eliminator-test-output.js')).read(), + ['eliminate']), + (path_from_root('tools', 'eliminator', 'safe-eliminator-test.js'), open(path_from_root('tools', 'eliminator', 'safe-eliminator-test-output.js')).read(), + ['eliminateMemSafe']), + (path_from_root('tools', 'eliminator', 'asm-eliminator-test.js'), open(path_from_root('tools', 'eliminator', 'asm-eliminator-test-output.js')).read(), + ['asm', 'eliminate']), + (path_from_root('tools', 'test-js-optimizer-asm-regs.js'), open(path_from_root('tools', 'test-js-optimizer-asm-regs-output.js')).read(), + ['asm', 'registerize']), + (path_from_root('tools', 'test-js-optimizer-asm-regs-min.js'), open(path_from_root('tools', 'test-js-optimizer-asm-regs-min-output.js')).read(), + ['asm', 'registerize']), + (path_from_root('tools', 'test-js-optimizer-asm-pre.js'), open(path_from_root('tools', 'test-js-optimizer-asm-pre-output.js')).read(), + ['asm', 'simplifyExpressions']), + (path_from_root('tools', 'test-js-optimizer-asm-last.js'), open(path_from_root('tools', 'test-js-optimizer-asm-last-output.js')).read(), + ['asm', 'last']), + (path_from_root('tools', 'test-js-optimizer-asm-relocate.js'), open(path_from_root('tools', 'test-js-optimizer-asm-relocate-output.js')).read(), + ['asm', 'relocate']), + (path_from_root('tools', 'test-js-optimizer-asm-outline1.js'), open(path_from_root('tools', 'test-js-optimizer-asm-outline1-output.js')).read(), + ['asm', 'outline']), + (path_from_root('tools', 'test-js-optimizer-asm-outline2.js'), open(path_from_root('tools', 'test-js-optimizer-asm-outline2-output.js')).read(), + ['asm', 'outline']), + (path_from_root('tools', 'test-js-optimizer-asm-outline3.js'), open(path_from_root('tools', 'test-js-optimizer-asm-outline3-output.js')).read(), + ['asm', 'outline']), + ]: + print input + output = Popen(listify(NODE_JS) + [path_from_root('tools', 'js-optimizer.js'), input] + passes, stdin=PIPE, stdout=PIPE).communicate()[0] + self.assertIdentical(expected, output.replace('\r\n', '\n').replace('\n\n', '\n')) + + def test_m_mm(self): + open(os.path.join(self.get_dir(), 'foo.c'), 'w').write('''#include ''') + for opt in ['M', 'MM']: + output, err = Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'foo.c'), '-' + opt], stdout=PIPE, stderr=PIPE).communicate() + assert 'foo.o: ' in output, '-%s failed to produce the right output: %s' % (opt, output) + assert 'error' not in err, 'Unexpected stderr: ' + err + + def test_chunking(self): + if os.environ.get('EMCC_DEBUG'): return self.skip('cannot run in debug mode') + if os.environ.get('EMCC_CORES'): return self.skip('cannot run if cores are altered') + if multiprocessing.cpu_count() < 2: return self.skip('need multiple cores') + try: + os.environ['EMCC_DEBUG'] = '1' + os.environ['EMCC_CORES'] = '2' + for asm, linkable, chunks, js_chunks in [ + (0, 0, 3, 2), (0, 1, 3, 4), + (1, 0, 3, 2), (1, 1, 3, 4) + ]: + print asm, linkable, chunks, js_chunks + output, err = Popen([PYTHON, EMCC, path_from_root('tests', 'hello_libcxx.cpp'), '-O1', '-s', 'LINKABLE=%d' % linkable, '-s', 'ASM_JS=%d' % asm] + (['-O2'] if asm else []), stdout=PIPE, stderr=PIPE).communicate() + ok = False + for c in range(chunks, chunks+2): + ok = ok or ('phase 2 working on %d chunks' % c in err) + assert ok, err + ok = False + for c in range(js_chunks, js_chunks+2): + ok = ok or ('splitting up js optimization into %d chunks' % c in err) + assert ok, err + finally: + del os.environ['EMCC_DEBUG'] + del os.environ['EMCC_CORES'] + + def test_debuginfo(self): + if os.environ.get('EMCC_DEBUG'): return self.skip('cannot run in debug mode') + try: + os.environ['EMCC_DEBUG'] = '1' + # llvm debug info is kept only when we can see it, which is without the js optimize, -O0. js debug info is lost by registerize in -O2, so - g disables it + for args, expect_llvm, expect_js in [ + (['-O0'], True, True), + (['-O0', '-g'], True, True), + (['-O1'], False, True), + (['-O1', '-g'], False, True), + (['-O2'], False, False), + (['-O2', '-g'], False, True), + ]: + print args, expect_llvm, expect_js + output, err = Popen([PYTHON, EMCC, path_from_root('tests', 'hello_world.cpp')] + args, stdout=PIPE, stderr=PIPE).communicate() + assert expect_llvm == ('strip-debug' not in err) + assert expect_js == ('registerize' not in err) + finally: + del os.environ['EMCC_DEBUG'] + + def test_scons(self): # also incidentally tests c++11 integration in llvm 3.1 + try_delete(os.path.join(self.get_dir(), 'test')) + shutil.copytree(path_from_root('tests', 'scons'), os.path.join(self.get_dir(), 'test')) + shutil.copytree(path_from_root('tools', 'scons', 'site_scons'), os.path.join(self.get_dir(), 'test', 'site_scons')) + os.chdir(os.path.join(self.get_dir(), 'test')) + Popen(['scons']).communicate() + output = run_js('scons_integration.js') + assert 'If you see this - the world is all right!' in output + + def test_embind(self): + for args, fail in [ + ([], True), # without --bind, we fail + (['--bind'], False), + (['--bind', '-O1'], False), + (['--bind', '-O2'], False), + (['--bind', '-O1', '-s', 'ASM_JS=0'], False), + (['--bind', '-O2', '-s', 'ASM_JS=0'], False) + ]: + print args, fail + self.clear() + try_delete(self.in_dir('a.out.js')) + Popen([PYTHON, EMCC, path_from_root('tests', 'embind', 'embind_test.cpp'), '--post-js', path_from_root('tests', 'embind', 'underscore-1.4.2.js'), '--post-js', path_from_root('tests', 'embind', 'imvu_test_adapter.js'), '--post-js', path_from_root('tests', 'embind', 'embind.test.js')] + args, stderr=PIPE if fail else None).communicate() + assert os.path.exists(self.in_dir('a.out.js')) == (not fail) + if not fail: + output = run_js(self.in_dir('a.out.js'), stdout=PIPE, stderr=PIPE, full_output=True) + assert "FAIL" not in output, output + + def test_llvm_nativizer(self): + try: + Popen(['as', '--version'], stdout=PIPE, stderr=PIPE).communicate() + except: + return self.skip('no gnu as, cannot run nativizer') + + # avoid impure_ptr problems etc. + shutil.copyfile(path_from_root('tests', 'files.cpp'), os.path.join(self.get_dir(), 'files.cpp')) + open(os.path.join(self.get_dir(), 'somefile.binary'), 'w').write('''waka waka############################''') + open(os.path.join(self.get_dir(), 'test.file'), 'w').write('''ay file..............,,,,,,,,,,,,,,''') + open(os.path.join(self.get_dir(), 'stdin'), 'w').write('''inter-active''') + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'files.cpp'), '-c']).communicate() + Popen([PYTHON, path_from_root('tools', 'nativize_llvm.py'), os.path.join(self.get_dir(), 'files.o')], stdout=PIPE, stderr=PIPE).communicate(input) + output = Popen([os.path.join(self.get_dir(), 'files.o.run')], stdin=open(os.path.join(self.get_dir(), 'stdin')), stdout=PIPE, stderr=PIPE).communicate() + self.assertContained('''size: 37 +data: 119,97,107,97,32,119,97,107,97,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35 +loop: 119 97 107 97 32 119 97 107 97 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 +input:inter-active +texto +$ +5 : 10,30,20,11,88 +other=ay file... +seeked= file. +''', output[0]) + self.assertIdentical('texte\n', output[1]) + + def test_emconfig(self): + output = Popen([PYTHON, EMCONFIG, 'LLVM_ROOT'], stdout=PIPE, stderr=PIPE).communicate()[0].strip() + try: + assert output == LLVM_ROOT + except: + print >> sys.stderr, 'Assertion failed: python %s LLVM_ROOT returned "%s" instead of expected "%s"!' % (EMCONFIG, output, LLVM_ROOT) + raise + invalid = 'Usage: em-config VAR_NAME' + # Don't accept variables that do not exist + output = Popen([PYTHON, EMCONFIG, 'VAR_WHICH_DOES_NOT_EXIST'], stdout=PIPE, stderr=PIPE).communicate()[0].strip() + assert output == invalid + # Don't accept no arguments + output = Popen([PYTHON, EMCONFIG], stdout=PIPE, stderr=PIPE).communicate()[0].strip() + assert output == invalid + # Don't accept more than one variable + output = Popen([PYTHON, EMCONFIG, 'LLVM_ROOT', 'EMCC'], stdout=PIPE, stderr=PIPE).communicate()[0].strip() + assert output == invalid + # Don't accept arbitrary python code + output = Popen([PYTHON, EMCONFIG, 'sys.argv[1]'], stdout=PIPE, stderr=PIPE).communicate()[0].strip() + assert output == invalid + + def test_link_s(self): + # -s OPT=VALUE can conflict with -s as a linker option. We warn and ignore + open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write(r''' + extern "C" { + void something(); + } + + int main() { + something(); + return 0; + } + ''') + open(os.path.join(self.get_dir(), 'supp.cpp'), 'w').write(r''' + #include + + extern "C" { + void something() { + printf("yello\n"); + } + } + ''') + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '-o', 'main.o']).communicate() + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'supp.cpp'), '-o', 'supp.o']).communicate() + + output = Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.o'), '-s', os.path.join(self.get_dir(), 'supp.o'), '-s', 'SAFE_HEAP=1'], stderr=PIPE).communicate() + self.assertContained('treating -s as linker option', output[1]) + output = run_js('a.out.js') + assert 'yello' in output, 'code works' + code = open('a.out.js').read() + assert 'SAFE_HEAP' in code, 'valid -s option had an effect' + + def test_jcache_printf(self): + open(self.in_dir('src.cpp'), 'w').write(r''' + #include + #include + #include + int main() { + emscripten_jcache_printf("hello world\n"); + emscripten_jcache_printf("hello %d world\n", 5); + emscripten_jcache_printf("hello %.3f world\n", 123.456789123); + emscripten_jcache_printf("hello %llx world\n", 0x1234567811223344ULL); + return 0; + } + ''') + Popen([PYTHON, EMCC, self.in_dir('src.cpp')]).communicate() + output = run_js('a.out.js') + self.assertIdentical('hello world\nhello 5 world\nhello 123.457 world\nhello 1234567811223300 world\n', output) + + def test_conftest_s_flag_passing(self): + open(os.path.join(self.get_dir(), 'conftest.c'), 'w').write(r''' + int main() { + return 0; + } + ''') + os.environ["EMMAKEN_JUST_CONFIGURE"] = "1" + cmd = [PYTHON, EMCC, '-s', 'ASSERTIONS=1', os.path.join(self.get_dir(), 'conftest.c'), '-o', 'conftest'] + output = Popen(cmd, stderr=PIPE).communicate() + del os.environ["EMMAKEN_JUST_CONFIGURE"] + self.assertNotContained('emcc: warning: treating -s as linker option', output[1]) + assert os.path.exists('conftest') + + def test_file_packager(self): + try: + os.mkdir('subdir') + except: + pass + open('data1.txt', 'w').write('data1') + os.chdir('subdir') + open('data2.txt', 'w').write('data2') + # relative path to below the current dir is invalid + out, err = Popen([PYTHON, FILE_PACKAGER, 'test.data', '--preload', '../data1.txt'], stdout=PIPE, stderr=PIPE).communicate() + assert len(out) == 0 + assert 'below the current directory' in err + # relative path that ends up under us is cool + out, err = Popen([PYTHON, FILE_PACKAGER, 'test.data', '--preload', '../subdir/data2.txt'], stdout=PIPE, stderr=PIPE).communicate() + assert len(out) > 0 + assert 'below the current directory' not in err + # direct path leads to the same code being generated - relative path does not make us do anything different + out2, err2 = Popen([PYTHON, FILE_PACKAGER, 'test.data', '--preload', 'data2.txt'], stdout=PIPE, stderr=PIPE).communicate() + assert len(out2) > 0 + assert 'below the current directory' not in err2 + def clean(txt): + return filter(lambda line: 'PACKAGE_UUID' not in line, txt.split('\n')) + out = clean(out) + out2 = clean(out2) + assert out == out2 + # sanity check that we do generate different code for different inputs + out3, err3 = Popen([PYTHON, FILE_PACKAGER, 'test.data', '--preload', 'data2.txt', 'data2.txt@waka.txt'], stdout=PIPE, stderr=PIPE).communicate() + out3 = clean(out3) + assert out != out3 + + def test_crunch(self): + # crunch should not be run if a .crn exists that is more recent than the .dds + shutil.copyfile(path_from_root('tests', 'ship.dds'), 'ship.dds') + time.sleep(0.1) + Popen([PYTHON, FILE_PACKAGER, 'test.data', '--pre-run', '--crunch=32', '--preload', 'ship.dds'], stdout=open('pre.js', 'w')).communicate() + assert os.stat('test.data').st_size < 0.25*os.stat('ship.dds').st_size, 'Compressed should be much smaller than dds' + crunch_time = os.stat('ship.crn').st_mtime + dds_time = os.stat('ship.dds').st_mtime + assert crunch_time > dds_time, 'Crunch is more recent' + # run again, should not recrunch! + time.sleep(0.1) + Popen([PYTHON, FILE_PACKAGER, 'test.data', '--pre-run', '--crunch=32', '--preload', 'ship.dds'], stdout=open('pre.js', 'w')).communicate() + assert crunch_time == os.stat('ship.crn').st_mtime, 'Crunch is unchanged' + # update dds, so should recrunch + time.sleep(0.1) + os.utime('ship.dds', None) + Popen([PYTHON, FILE_PACKAGER, 'test.data', '--pre-run', '--crunch=32', '--preload', 'ship.dds'], stdout=open('pre.js', 'w')).communicate() + assert crunch_time < os.stat('ship.crn').st_mtime, 'Crunch was changed' + + def test_headless(self): + if SPIDERMONKEY_ENGINE not in JS_ENGINES: return self.skip('cannot run without spidermonkey due to node limitations (Uint8ClampedArray etc.)') + + shutil.copyfile(path_from_root('tests', 'screenshot.png'), os.path.join(self.get_dir(), 'example.png')) + Popen([PYTHON, EMCC, path_from_root('tests', 'sdl_canvas.c'), '-s', 'HEADLESS=1']).communicate() + output = run_js('a.out.js', engine=SPIDERMONKEY_ENGINE, stderr=PIPE) + assert '''Init: 0 +Font: 0x1 +Sum: 0 +you should see two lines of text in different colors and a blue rectangle +SDL_Quit called (and ignored) +done. +''' in output, output diff --git a/tests/test_sanity.py b/tests/test_sanity.py new file mode 100644 index 0000000000000..931645e29c25c --- /dev/null +++ b/tests/test_sanity.py @@ -0,0 +1,522 @@ +import os, shutil, stat, subprocess +from runner import RunnerCore, path_from_root +from tools.shared import * + +SANITY_FILE = CONFIG_FILE + '_sanity' +commands = [[EMCC], [PYTHON, path_from_root('tests', 'runner.py'), 'blahblah']] + +def restore(): + shutil.copyfile(CONFIG_FILE + '_backup', CONFIG_FILE) + +def wipe(): + try_delete(CONFIG_FILE) + try_delete(SANITY_FILE) + +def mtime(filename): + return os.stat(filename).st_mtime + +class sanity(RunnerCore): + @classmethod + def setUpClass(self): + super(RunnerCore, self).setUpClass() + shutil.copyfile(CONFIG_FILE, CONFIG_FILE + '_backup') + + print + print 'Running sanity checks.' + print 'WARNING: This will modify %s, and in theory can break it although it should be restored properly. A backup will be saved in %s_backup' % (EM_CONFIG, EM_CONFIG) + print + + assert os.path.exists(CONFIG_FILE), 'To run these tests, we need a (working!) %s file to already exist' % EM_CONFIG + assert not os.environ.get('EMCC_DEBUG'), 'do not run sanity checks in debug mode!' + + @classmethod + def tearDownClass(self): + super(RunnerCore, self).tearDownClass() + + def setUp(self): + wipe() + + def tearDown(self): + restore() + + def do(self, command): + if type(command) is not list: + command = [command] + if command[0] == EMCC: + command = [PYTHON] + command + + return Popen(command, stdout=PIPE, stderr=STDOUT).communicate()[0] + + def check_working(self, command, expected=None): + if type(command) is not list: + command = [command] + if expected is None: + if command[0] == EMCC: + expected = 'no input files' + else: + expected = "No tests found for ['blahblah']" + + output = self.do(command) + self.assertContained(expected, output) + return output + + def test_aaa_normal(self): # this should be the very first thing that runs. if this fails, everything else is irrelevant! + for command in commands: + # Your existing EM_CONFIG should work! + restore() + self.check_working(command) + + def test_firstrun(self): + for command in commands: + wipe() + + def make_executable(name): + with open(os.path.join(temp_bin, name), 'w') as f: + os.fchmod(f.fileno(), stat.S_IRWXU) + + try: + temp_bin = tempfile.mkdtemp() + old_environ_path = os.environ['PATH'] + os.environ['PATH'] = temp_bin + os.pathsep + old_environ_path + make_executable('llvm-dis') + make_executable('node') + make_executable('python2') + output = self.do(command) + finally: + os.environ['PATH'] = old_environ_path + shutil.rmtree(temp_bin) + + self.assertContained('Welcome to Emscripten!', output) + self.assertContained('This is the first time any of the Emscripten tools has been run.', output) + self.assertContained('A settings file has been copied to %s, at absolute path: %s' % (EM_CONFIG, CONFIG_FILE), output) + self.assertContained('It contains our best guesses for the important paths, which are:', output) + self.assertContained('LLVM_ROOT', output) + self.assertContained('NODE_JS', output) + self.assertContained('PYTHON', output) + if platform.system() is not 'Windows': + # os.chmod can't make files executable on Windows + self.assertIdentical(temp_bin, re.search("^ *LLVM_ROOT *= (.*)$", output, re.M).group(1)) + self.assertIdentical(os.path.join(temp_bin, 'node'), re.search("^ *NODE_JS *= (.*)$", output, re.M).group(1)) + self.assertIdentical(os.path.join(temp_bin, 'python2'), re.search("^ *PYTHON *= (.*)$", output, re.M).group(1)) + self.assertContained('Please edit the file if any of those are incorrect', output) + self.assertContained('This command will now exit. When you are done editing those paths, re-run it.', output) + assert output.split()[-1].endswith('===='), 'We should have stopped: ' + output + config_file = open(CONFIG_FILE).read() + template_file = open(path_from_root('tools', 'settings_template_readonly.py')).read() + self.assertNotContained('~/.emscripten', config_file) + self.assertContained('~/.emscripten', template_file) + self.assertNotContained('{{{', config_file) + self.assertNotContained('}}}', config_file) + self.assertContained('{{{', template_file) + self.assertContained('}}}', template_file) + for content in ['EMSCRIPTEN_ROOT', 'LLVM_ROOT', 'NODE_JS', 'TEMP_DIR', 'COMPILER_ENGINE', 'JS_ENGINES']: + self.assertContained(content, config_file) + + # The guessed config should be ok XXX This depends on your local system! it is possible `which` guesses wrong + #try_delete('a.out.js') + #output = Popen([PYTHON, EMCC, path_from_root('tests', 'hello_world.c')], stdout=PIPE, stderr=PIPE).communicate() + #self.assertContained('hello, world!', run_js('a.out.js'), output) + + # Second run, with bad EM_CONFIG + for settings in ['blah', 'LLVM_ROOT="blarg"; JS_ENGINES=[]; COMPILER_ENGINE=NODE_JS=SPIDERMONKEY_ENGINE=[]']: + f = open(CONFIG_FILE, 'w') + f.write(settings) + f.close() + output = self.do(command) + + if 'LLVM_ROOT' not in settings: + self.assertContained('Error in evaluating %s' % EM_CONFIG, output) + elif 'runner.py' not in ' '.join(command): + self.assertContained('CRITICAL', output) # sanity check should fail + + def test_closure_compiler(self): + CLOSURE_FATAL = 'fatal: Closure compiler' + CLOSURE_WARNING = 'does not exist' + + # Sanity check should find closure + restore() + output = self.check_working(EMCC) + self.assertNotContained(CLOSURE_FATAL, output) + self.assertNotContained(CLOSURE_WARNING, output) + + # Append a bad path for closure, will warn + f = open(CONFIG_FILE, 'a') + f.write('CLOSURE_COMPILER = "/tmp/nowhere/nothingtoseehere/kjadsfkjwelkjsdfkqgas/nonexistent.txt"\n') + f.close() + output = self.check_working(EMCC, CLOSURE_WARNING) + + # And if you actually try to use the bad path, will be fatal + f = open(CONFIG_FILE, 'a') + f.write('CLOSURE_COMPILER = "/tmp/nowhere/nothingtoseehere/kjadsfkjwelkjsdfkqgas/nonexistent.txt"\n') + f.close() + output = self.check_working([EMCC, '-O2', '-s', 'ASM_JS=0', '--closure', '1', 'tests/hello_world.cpp'], CLOSURE_FATAL) + + # With a working path, all is well + restore() + try_delete('a.out.js') + output = self.check_working([EMCC, '-O2', '-s', 'ASM_JS=0', '--closure', '1', 'tests/hello_world.cpp'], '') + assert os.path.exists('a.out.js'), output + + def test_llvm(self): + LLVM_WARNING = 'LLVM version appears incorrect' + + restore() + + # Clang should report the version number we expect, and emcc should not warn + assert check_clang_version() + output = self.check_working(EMCC) + assert LLVM_WARNING not in output, output + + # Fake a different llvm version + restore() + f = open(CONFIG_FILE, 'a') + f.write('LLVM_ROOT = "' + path_from_root('tests', 'fake') + '"') + f.close() + + if not os.path.exists(path_from_root('tests', 'fake')): + os.makedirs(path_from_root('tests', 'fake')) + + try: + os.environ['EM_IGNORE_SANITY'] = '1' + for x in range(-2, 3): + for y in range(-2, 3): + f = open(path_from_root('tests', 'fake', 'clang'), 'w') + f.write('#!/bin/sh\n') + f.write('echo "clang version %d.%d" 1>&2\n' % (EXPECTED_LLVM_VERSION[0] + x, EXPECTED_LLVM_VERSION[1] + y)) + f.close() + shutil.copyfile(path_from_root('tests', 'fake', 'clang'), path_from_root('tests', 'fake', 'clang++')) + os.chmod(path_from_root('tests', 'fake', 'clang'), stat.S_IREAD | stat.S_IWRITE | stat.S_IEXEC) + os.chmod(path_from_root('tests', 'fake', 'clang++'), stat.S_IREAD | stat.S_IWRITE | stat.S_IEXEC) + if x != 0 or y != 0: + output = self.check_working(EMCC, LLVM_WARNING) + else: + output = self.check_working(EMCC) + assert LLVM_WARNING not in output, output + finally: + del os.environ['EM_IGNORE_SANITY'] + + def test_node(self): + NODE_WARNING = 'node version appears too old' + NODE_WARNING_2 = 'cannot check node version' + + restore() + + # Clang should report the version number we expect, and emcc should not warn + assert check_node_version() + output = self.check_working(EMCC) + assert NODE_WARNING not in output, output + + # Fake a different node version + restore() + f = open(CONFIG_FILE, 'a') + f.write('NODE_JS = "' + path_from_root('tests', 'fake', 'nodejs') + '"') + f.close() + + if not os.path.exists(path_from_root('tests', 'fake')): + os.makedirs(path_from_root('tests', 'fake')) + + try: + os.environ['EM_IGNORE_SANITY'] = '1' + for version, succeed in [('v0.7.9', False), ('v0.8.0', True), ('v0.8.1', True), ('cheez', False)]: + f = open(path_from_root('tests', 'fake', 'nodejs'), 'w') + f.write('#!/bin/sh\n') + f.write('''if [ $1 = "--version" ]; then +echo "%s" +else +%s $@ +fi +''' % (version, NODE_JS)) + f.close() + os.chmod(path_from_root('tests', 'fake', 'nodejs'), stat.S_IREAD | stat.S_IWRITE | stat.S_IEXEC) + if not succeed: + if version[0] == 'v': + self.check_working(EMCC, NODE_WARNING) + else: + self.check_working(EMCC, NODE_WARNING_2) + else: + output = self.check_working(EMCC) + assert NODE_WARNING not in output, output + finally: + del os.environ['EM_IGNORE_SANITY'] + + def test_emcc(self): + SANITY_MESSAGE = 'Emscripten: Running sanity checks' + SANITY_FAIL_MESSAGE = 'sanity check failed to run' + + # emcc should check sanity if no ${EM_CONFIG}_sanity + restore() + time.sleep(0.1) + assert not os.path.exists(SANITY_FILE) # restore is just the settings, not the sanity + output = self.check_working(EMCC) + self.assertContained(SANITY_MESSAGE, output) + assert os.path.exists(SANITY_FILE) # EMCC should have checked sanity successfully + assert mtime(SANITY_FILE) >= mtime(CONFIG_FILE) + assert generate_sanity() == open(SANITY_FILE).read() + self.assertNotContained(SANITY_FAIL_MESSAGE, output) + + # emcc run again should not sanity check, because the sanity file is newer + output = self.check_working(EMCC) + self.assertNotContained(SANITY_MESSAGE, output) + self.assertNotContained(SANITY_FAIL_MESSAGE, output) + + # correct sanity contents mean we need not check + open(SANITY_FILE, 'w').write(generate_sanity()) + output = self.check_working(EMCC) + self.assertNotContained(SANITY_MESSAGE, output) + + # incorrect sanity contents mean we *must* check + open(SANITY_FILE, 'w').write('wakawaka') + output = self.check_working(EMCC) + self.assertContained(SANITY_MESSAGE, output) + + # but with EMCC_DEBUG=1 we should check + try: + os.environ['EMCC_DEBUG'] = '1' + output = self.check_working(EMCC) + finally: + del os.environ['EMCC_DEBUG'] + self.assertContained(SANITY_MESSAGE, output) + output = self.check_working(EMCC) + self.assertNotContained(SANITY_MESSAGE, output) + + # Make sure the test runner didn't do anything to the setup + output = self.check_working(EMCC) + self.assertNotContained(SANITY_MESSAGE, output) + self.assertNotContained(SANITY_FAIL_MESSAGE, output) + + # emcc should also check sanity if the file is outdated + time.sleep(0.1) + restore() + assert mtime(SANITY_FILE) < mtime(CONFIG_FILE) + output = self.check_working(EMCC) + self.assertContained(SANITY_MESSAGE, output) + assert mtime(SANITY_FILE) >= mtime(CONFIG_FILE) + self.assertNotContained(SANITY_FAIL_MESSAGE, output) + + # emcc should be configurable directly from EM_CONFIG without any config file + restore() + config = open(CONFIG_FILE, 'r').read() + os.environ['EM_CONFIG'] = config + wipe() + dirname = tempfile.mkdtemp(prefix='emscripten_test_' + self.__class__.__name__ + '_', dir=TEMP_DIR) + open(os.path.join(dirname, 'main.cpp'), 'w').write(''' + #include + int main() { + printf("hello from emcc with no config file\\n"); + return 0; + } + ''') + Popen([PYTHON, EMCC, os.path.join(dirname, 'main.cpp'), '-o', os.path.join(dirname, 'a.out.js')]).communicate() + del os.environ['EM_CONFIG'] + old_dir = os.getcwd() + try: + os.chdir(dirname) + self.assertContained('hello from emcc with no config file', run_js('a.out.js')) + finally: + os.chdir(old_dir) + shutil.rmtree(dirname) + + try_delete(CANONICAL_TEMP_DIR) + + def test_emcc_caching(self): + INCLUDING_MESSAGE = 'including X' + BUILDING_MESSAGE = 'building X for cache' + ERASING_MESSAGE = 'clearing cache' + + EMCC_CACHE = Cache.dirname + + for compiler in [EMCC, EMXX]: + print compiler + + restore() + + Cache.erase() + assert not os.path.exists(EMCC_CACHE) + + try: + os.environ['EMCC_DEBUG'] ='1' + self.working_dir = os.path.join(TEMP_DIR, 'emscripten_temp') + + # Building a file that doesn't need cached stuff should not trigger cache generation + output = self.do([compiler, path_from_root('tests', 'hello_world.cpp')]) + assert INCLUDING_MESSAGE.replace('X', 'libc') not in output + assert BUILDING_MESSAGE.replace('X', 'libc') not in output + self.assertContained('hello, world!', run_js('a.out.js')) + assert not os.path.exists(EMCC_CACHE) + try_delete('a.out.js') + + basebc_name = os.path.join(TEMP_DIR, 'emscripten_temp', 'emcc-0-basebc.bc') + dcebc_name = os.path.join(TEMP_DIR, 'emscripten_temp', 'emcc-1-linktime.bc') + ll_names = [os.path.join(TEMP_DIR, 'emscripten_temp', 'emcc-X-ll.ll').replace('X', str(x)) for x in range(2,5)] + + # Building a file that *does* need dlmalloc *should* trigger cache generation, but only the first time + for filename, libname in [('hello_malloc.cpp', 'libc'), ('hello_libcxx.cpp', 'libcxx')]: + for i in range(3): + print filename, libname, i + self.clear() + try_delete(basebc_name) # we might need to check this file later + try_delete(dcebc_name) # we might need to check this file later + for ll_name in ll_names: try_delete(ll_name) + output = self.do([compiler, '-O' + str(i), '-s', 'RELOOP=0', '--llvm-lto', '0', path_from_root('tests', filename)]) + #print output + assert INCLUDING_MESSAGE.replace('X', libname) in output + if libname == 'libc': + assert INCLUDING_MESSAGE.replace('X', 'libcxx') not in output # we don't need libcxx in this code + else: + assert INCLUDING_MESSAGE.replace('X', 'libc') in output # libcxx always forces inclusion of libc + assert (BUILDING_MESSAGE.replace('X', libname) in output) == (i == 0), 'Must only build the first time' + self.assertContained('hello, world!', run_js('a.out.js')) + assert os.path.exists(EMCC_CACHE) + assert os.path.exists(os.path.join(EMCC_CACHE, libname + '.bc')) + if libname == 'libcxx': + print os.stat(os.path.join(EMCC_CACHE, libname + '.bc')).st_size, os.stat(basebc_name).st_size, os.stat(dcebc_name).st_size + assert os.stat(os.path.join(EMCC_CACHE, libname + '.bc')).st_size > 1000000, 'libc++ is big' + assert os.stat(basebc_name).st_size > 1000000, 'libc++ is indeed big' + assert os.stat(dcebc_name).st_size < os.stat(basebc_name).st_size/2, 'Dead code elimination must remove most of libc++' + # should only have metadata in -O0, not 1 and 2 + if i > 0: + for ll_name in ll_names: + ll = None + try: + ll = open(ll_name).read() + break + except: + pass + assert ll + assert ll.count('\n!') < 10 # a few lines are left even in -O1 and -O2 + finally: + del os.environ['EMCC_DEBUG'] + + restore() + + def ensure_cache(): + self.do([EMCC, '-O2', path_from_root('tests', 'hello_world.c')]) + + # Manual cache clearing + ensure_cache() + assert os.path.exists(EMCC_CACHE) + output = self.do([EMCC, '--clear-cache']) + assert ERASING_MESSAGE in output + assert not os.path.exists(EMCC_CACHE) + + # Changing LLVM_ROOT, even without altering .emscripten, clears the cache + ensure_cache() + old = os.environ.get('LLVM') + try: + os.environ['LLVM'] = 'waka' + assert os.path.exists(EMCC_CACHE) + output = self.do([EMCC]) + assert ERASING_MESSAGE in output + assert not os.path.exists(EMCC_CACHE) + finally: + if old: os.environ['LLVM'] = old + else: del os.environ['LLVM'] + + try_delete(CANONICAL_TEMP_DIR) + + def test_relooper(self): + RELOOPER = Cache.get_path('relooper.js') + + restore() + for phase in range(2): # 0: we wipe the relooper dir. 1: we have it, so should just update + if phase == 0: Cache.erase() + try_delete(RELOOPER) + + for i in range(4): + print >> sys.stderr, phase, i + opt = min(i, 2) + try_delete('a.out.js') + output = Popen([PYTHON, EMCC, path_from_root('tests', 'hello_world_loop.cpp'), '-O' + str(opt), '-g'], + stdout=PIPE, stderr=PIPE).communicate() + self.assertContained('hello, world!', run_js('a.out.js')) + output = '\n'.join(output) + assert ('bootstrapping relooper succeeded' in output) == (i == 2), 'only bootstrap on first O2: ' + output + assert os.path.exists(RELOOPER) == (i >= 2), 'have relooper on O2: ' + output + src = open('a.out.js').read() + main = src.split('function _main()')[1].split('\n}\n')[0] + assert ('while (1) {' in main or 'while(1){' in main or '} while ($' in main or '}while($' in main) == (i >= 2), 'reloop code on O2: ' + main + assert ('switch' not in main) == (i >= 2), 'reloop code on O2: ' + main + + def test_jcache(self): + PRE_LOAD_MSG = 'loading pre from jcache' + PRE_SAVE_MSG = 'saving pre to jcache' + FUNC_CHUNKS_LOAD_MSG = ' funcchunks from jcache' + FUNC_CHUNKS_SAVE_MSG = ' funcchunks to jcache' + JSFUNC_CHUNKS_LOAD_MSG = 'jsfuncchunks from jcache' + JSFUNC_CHUNKS_SAVE_MSG = 'jsfuncchunks to jcache' + + restore() + Cache.erase() + + try: + os.environ['EMCC_DEBUG'] = '1' + os.environ['EMCC_JSOPT_MIN_CHUNK_SIZE'] = str(1024*512) + + self.working_dir = os.path.join(TEMP_DIR, 'emscripten_temp') + if not os.path.exists(self.working_dir): os.makedirs(self.working_dir) + + assert not os.path.exists(JCache.get_cachename('emscript_files')) + + srcs = {} + used_jcache = False + + for args, input_file, expect_pre_save, expect_pre_load, expect_funcs_save, expect_funcs_load, expect_jsfuncs_save, expect_jsfuncs_load, expected in [ + ([], 'hello_world_loop.cpp', False, False, False, False, False, False, []), + (['--jcache'], 'hello_world_loop.cpp', True, False, True, False, True, False, []), + (['--jcache'], 'hello_world_loop.cpp', False, True, False, True, False, True, []), + ([], 'hello_world_loop.cpp', False, False, False, False, False, False, []), + # new + ([], 'hello_world.cpp', False, False, False, False, False, False, []), + (['--jcache'], 'hello_world.cpp', True, False, True, False, True, False, []), + (['--jcache'], 'hello_world.cpp', False, True, False, True, False, True, []), + ([], 'hello_world.cpp', False, False, False, False, False, False, []), + # go back to old file, experience caching + (['--jcache'], 'hello_world_loop.cpp', False, True, False, True, False, True, []), + # new, large file + ([], 'hello_malloc.cpp', False, False, False, False, False, False, []), + (['--jcache'], 'hello_malloc.cpp', True, False, True, False, True, False, []), + (['--jcache'], 'hello_malloc.cpp', False, True, False, True, False, True, []), + ([], 'hello_malloc.cpp', False, False, False, False, False, False, []), + # new, huge file + ([], 'hello_libcxx.cpp', False, False, False, False, False, False, ('3 chunks',)), + (['--jcache'], 'hello_libcxx.cpp', True, False, True, False, True, False, []), + (['--jcache'], 'hello_libcxx.cpp', False, True, False, True, False, True, []), + ([], 'hello_libcxx.cpp', False, False, False, False, False, False, []), + # finally, build a file close to the previous, to see that some chunks are found in the cache and some not + (['--jcache'], 'hello_libcxx_mod1.cpp', False, True, True, True, True, True, []), # win on pre, mix on funcs, mix on jsfuncs + (['--jcache'], 'hello_libcxx_mod1.cpp', False, True, False, True, False, True, []), + (None, None, None, None, None, None, None, None, None), # clear + (['--jcache'], 'hello_libcxx_mod2.cpp', True, False, True, False, True, False, []), # load into cache + (['--jcache'], 'hello_libcxx_mod2a.cpp', False, True, True, True, True, True, []) # add a printf, do not lose everything + ]: + self.clear() + if args is None: + Cache.erase() + continue + + print >> sys.stderr, args, input_file, expect_pre_save, expect_pre_load, expect_funcs_save, expect_funcs_load, expect_jsfuncs_save, expect_jsfuncs_load, expected + + out, err = Popen([PYTHON, EMCC, '-O2', '-g', path_from_root('tests', input_file)] + args, stdout=PIPE, stderr=PIPE).communicate() + errtail = err.split('emcc invocation')[-1] + self.assertContained('hello, world!', run_js('a.out.js'), errtail) + assert (PRE_SAVE_MSG in err) == expect_pre_save, errtail + assert (PRE_LOAD_MSG in err) == expect_pre_load, errtail + assert (FUNC_CHUNKS_SAVE_MSG in err) == expect_funcs_save, errtail + assert (FUNC_CHUNKS_LOAD_MSG in err) == expect_funcs_load, errtail + assert (JSFUNC_CHUNKS_SAVE_MSG in err) == expect_jsfuncs_save, errtail + assert (JSFUNC_CHUNKS_LOAD_MSG in err) == expect_jsfuncs_load, errtail + for expect in expected: assert expect in err, expect + ' ? ' + errtail + curr = open('a.out.js').read() + if input_file not in srcs: + srcs[input_file] = curr + else: + #open('/home/alon/Dev/emscripten/a', 'w').write(srcs[input_file]) + #open('/home/alon/Dev/emscripten/b', 'w').write(curr) + assert abs(len(curr)/float(len(srcs[input_file]))-1)<0.01, 'contents may shift in order, but must remain the same size %d vs %d' % (len(curr), len(srcs[input_file])) + '\n' + errtail + used_jcache = used_jcache or ('--jcache' in args) + assert used_jcache == os.path.exists(JCache.get_cachename('emscript_files')) + #print >> sys.stderr, errtail + + finally: + del os.environ['EMCC_DEBUG'] + del os.environ['EMCC_JSOPT_MIN_CHUNK_SIZE'] \ No newline at end of file diff --git a/tests/test_sockets.py b/tests/test_sockets.py new file mode 100644 index 0000000000000..0359367454332 --- /dev/null +++ b/tests/test_sockets.py @@ -0,0 +1,200 @@ +import os, multiprocessing, subprocess +from runner import BrowserCore, path_from_root +from tools.shared import * + +def clean_pids(pids): + import signal, errno + def pid_exists(pid): + try: + # NOTE: may just kill the process in Windows + os.kill(pid, 0) + except OSError, e: + return e.errno == errno.EPERM + else: + return True + def kill_pids(pids, sig): + for pid in pids: + if not pid_exists(pid): + break + print '[killing %d]' % pid + try: + os.kill(pid, sig) + print '[kill succeeded]' + except: + print '[kill fail]' + # ask nicely (to try and catch the children) + kill_pids(pids, signal.SIGTERM) + time.sleep(1) + # extreme prejudice, may leave children + kill_pids(pids, signal.SIGKILL) + +def make_relay_server(port1, port2): + print >> sys.stderr, 'creating relay server on ports %d,%d' % (port1, port2) + proc = Popen([PYTHON, path_from_root('tests', 'sockets', 'socket_relay.py'), str(port1), str(port2)]) + return proc + +class WebsockifyServerHarness: + def __init__(self, filename, args, listen_port, target_port): + self.pids = [] + self.filename = filename + self.target_port = target_port + self.listen_port = listen_port + self.args = args or [] + + def __enter__(self): + import socket, websockify + + # compile the server + # NOTE empty filename support is a hack to support + # the current test_enet + if self.filename: + Popen([CLANG_CC, path_from_root('tests', self.filename), '-o', 'server'] + self.args).communicate() + process = Popen([os.path.abspath('server')]) + self.pids.append(process.pid) + + # start the websocket proxy + print >> sys.stderr, 'running websockify on %d, forward to tcp %d' % (self.listen_port, self.target_port) + wsp = websockify.WebSocketProxy(verbose=True, listen_port=self.listen_port, target_host="127.0.0.1", target_port=self.target_port, run_once=True) + self.websockify = multiprocessing.Process(target=wsp.start_server) + self.websockify.start() + self.pids.append(self.websockify.pid) + print '[Websockify on process %s]' % str(self.pids[-2:]) + + def __exit__(self, *args, **kwargs): + # try to kill the websockify proxy gracefully + if self.websockify.is_alive(): + self.websockify.terminate() + self.websockify.join() + + # clean up any processes we started + clean_pids(self.pids) + + +class CompiledServerHarness: + def __init__(self, filename, args): + self.pids = [] + self.filename = filename + self.args = args or [] + + def __enter__(self): + import socket, websockify + + # compile the server + Popen([PYTHON, EMCC, path_from_root('tests', self.filename), '-o', 'server.js'] + self.args).communicate() + process = Popen([NODE_JS, 'server.js']) + self.pids.append(process.pid) + + def __exit__(self, *args, **kwargs): + # clean up any processes we started + clean_pids(self.pids) + + # always run these tests last + # make sure to use different ports in each one because it takes a while for the processes to be cleaned up + + # NOTE all datagram tests are temporarily disabled, as + # we can't truly test datagram sockets until we have + # proper listen server support. + +class sockets(BrowserCore): + def test_sockets_echo(self): + sockets_include = '-I'+path_from_root('tests', 'sockets') + + for datagram in [0]: + dgram_define = '-DTEST_DGRAM=%d' % datagram + + for harness in [ + WebsockifyServerHarness(os.path.join('sockets', 'test_sockets_echo_server.c'), ['-DSOCKK=8990', dgram_define, sockets_include], 8991, 8990) + # CompiledServerHarness(os.path.join('sockets', 'test_sockets_echo_server.c'), ['-DSOCKK=8990', dgram_define, sockets_include]) + ]: + with harness: + self.btest(os.path.join('sockets', 'test_sockets_echo_client.c'), expected='0', args=['-DSOCKK=8991', dgram_define, sockets_include]) + + def test_sockets_echo_bigdata(self): + sockets_include = '-I'+path_from_root('tests', 'sockets') + + for datagram in [0]: + dgram_define = '-DTEST_DGRAM=%d' % datagram + + # generate a large string literal to use as our message + message = '' + for i in range(256*256*2): + message += str(unichr(ord('a') + (i % 26))) + + # re-write the client test with this literal (it's too big to pass via command line) + input_filename = path_from_root('tests', 'sockets', 'test_sockets_echo_client.c') + input = open(input_filename).read() + output = input.replace('#define MESSAGE "pingtothepong"', '#define MESSAGE "%s"' % message) + + for harness in [ + WebsockifyServerHarness(os.path.join('sockets', 'test_sockets_echo_server.c'), ['-DSOCKK=8992', dgram_define, sockets_include], 8993, 8992) + ]: + with harness: + self.btest(output, expected='0', args=['-DSOCKK=8993', dgram_define, sockets_include], force_c=True) + + def test_sockets_partial(self): + for harness in [ + WebsockifyServerHarness(os.path.join('sockets', 'test_sockets_partial_server.c'), ['-DSOCKK=8994'], 8995, 8994) + ]: + with harness: + self.btest(os.path.join('sockets', 'test_sockets_partial_client.c'), expected='165', args=['-DSOCKK=8995']) + + # TODO add support for gethostbyaddr to re-enable this test + # def test_sockets_gethostbyname(self): + # self.btest(os.path.join('sockets', 'test_sockets_gethostbyname.c'), expected='0', args=['-O2', '-DSOCKK=8997']) + + def test_sockets_select_server_no_accept(self): + for harness in [ + WebsockifyServerHarness(os.path.join('sockets', 'test_sockets_select_server_no_accept_server.c'), ['-DSOCKK=8995'], 8996, 8995) + ]: + self.btest(os.path.join('sockets', 'test_sockets_select_server_no_accept_client.c'), expected='266', args=['-DSOCKK=8996']) + + def test_sockets_select_server_closes_connection_rw(self): + sockets_include = '-I'+path_from_root('tests', 'sockets') + + for harness in [ + WebsockifyServerHarness(os.path.join('sockets', 'test_sockets_echo_server.c'), ['-DSOCKK=9004', sockets_include], 9005, 9004) + ]: + with harness: + self.btest(os.path.join('sockets', 'test_sockets_select_server_closes_connection_client_rw.c'), expected='266', args=['-DSOCKK=9005', sockets_include]) + + # TODO remove this once we have proper listen server support built into emscripten. + # being that enet uses datagram sockets, we can't proxy to a native server with + # websockify, so we're emulating the listen server in the browser and relaying + # between two TCP servers. + def test_enet(self): + try_delete(self.in_dir('enet')) + shutil.copytree(path_from_root('tests', 'enet'), self.in_dir('enet')) + pwd = os.getcwd() + os.chdir(self.in_dir('enet')) + Popen([PYTHON, path_from_root('emconfigure'), './configure']).communicate() + Popen([PYTHON, path_from_root('emmake'), 'make']).communicate() + enet = [self.in_dir('enet', '.libs', 'libenet.a'), '-I'+path_from_root('tests', 'enet', 'include')] + os.chdir(pwd) + Popen([PYTHON, EMCC, path_from_root('tests', 'sockets', 'test_enet_server.c'), '-o', 'server.html', '-DSOCKK=2235'] + enet).communicate() + + with WebsockifyServerHarness('', [], 2235, 2234): + with WebsockifyServerHarness('', [], 2237, 2236): + pids = [] + try: + proc = make_relay_server(2234, 2236) + pids.append(proc.pid) + self.btest(os.path.join('sockets', 'test_enet_client.c'), expected='0', args=['-DSOCKK=2237'] + enet) + finally: + clean_pids(pids); + + # TODO use this once we have listen server support + # def test_enet(self): + # try_delete(self.in_dir('enet')) + # shutil.copytree(path_from_root('tests', 'enet'), self.in_dir('enet')) + # pwd = os.getcwd() + # os.chdir(self.in_dir('enet')) + # Popen([PYTHON, path_from_root('emconfigure'), './configure']).communicate() + # Popen([PYTHON, path_from_root('emmake'), 'make']).communicate() + # enet = [self.in_dir('enet', '.libs', 'libenet.a'), '-I'+path_from_root('tests', 'enet', 'include')] + # os.chdir(pwd) + + # for harness in [ + # self.CompiledServerHarness(os.path.join('sockets', 'test_enet_server.c'), ['-DSOCKK=9010'] + enet, 9011, 9010) + # ]: + # with harness: + # self.btest(os.path.join('sockets', 'test_enet_client.c'), expected='0', args=['-DSOCKK=9011'] + enet) diff --git a/tools/shared.py b/tools/shared.py index 08231af9b45ad..b8a796acddaea 100644 --- a/tools/shared.py +++ b/tools/shared.py @@ -674,62 +674,89 @@ def expand_response(data): # Settings. A global singleton. Not pretty, but nicer than passing |, settings| everywhere -class Settings: - @classmethod - def reset(self): - class Settings2: - QUANTUM_SIZE = 4 - reset = Settings.reset - - # Given some emcc-type args (-O3, -s X=Y, etc.), fill Settings with the right settings - @classmethod - def load(self, args=[]): - # Load the JS defaults into python - settings = open(path_from_root('src', 'settings.js')).read().replace('var ', 'Settings.').replace('//', '#') - exec settings in globals() - - # Apply additional settings. First -O, then -s - for i in range(len(args)): - if args[i].startswith('-O'): - level = eval(args[i][2]) - Settings.apply_opt_level(level) - for i in range(len(args)): - if args[i] == '-s': - exec 'Settings.' + args[i+1] in globals() # execute the setting - - # Transforms the Settings information into emcc-compatible args (-s X=Y, etc.). Basically - # the reverse of load_settings, except for -Ox which is relevant there but not here - @classmethod - def serialize(self): - ret = [] - for key, value in Settings.__dict__.iteritems(): - if key == key.upper(): # this is a hack. all of our settings are ALL_CAPS, python internals are not - jsoned = json.dumps(value, sort_keys=True) - ret += ['-s', key + '=' + jsoned] - return ret - - @classmethod - def apply_opt_level(self, opt_level, noisy=False): - if opt_level >= 1: - Settings.ASM_JS = 1 - Settings.ASSERTIONS = 0 - Settings.DISABLE_EXCEPTION_CATCHING = 1 - Settings.EMIT_GENERATED_FUNCTIONS = 1 - if opt_level >= 2: - Settings.RELOOP = 1 - Settings.ALIASING_FUNCTION_POINTERS = 1 - if opt_level >= 3: - # Aside from these, -O3 also runs closure compiler and llvm lto - Settings.FORCE_ALIGNED_MEMORY = 1 - Settings.DOUBLE_MODE = 0 - Settings.PRECISE_I64_MATH = 0 - if noisy: logging.warning('Applying some potentially unsafe optimizations! (Use -O2 if this fails.)') - - global Settings - Settings = Settings2 - Settings.load() # load defaults - -Settings.reset() +class Settings2(type): + class __impl: + attrs = {} + + def __init__(self): + self.reset() + + @classmethod + def reset(self): + self.attrs = { 'QUANTUM_SIZE': 4 } + self.load() + + # Given some emcc-type args (-O3, -s X=Y, etc.), fill Settings with the right settings + @classmethod + def load(self, args=[]): + # Load the JS defaults into python + settings = open(path_from_root('src', 'settings.js')).read().replace('//', '#') + settings = re.sub(r'var ([\w\d]+)', r'self.attrs["\1"]', settings) + exec settings + + # Apply additional settings. First -O, then -s + for i in range(len(args)): + if args[i].startswith('-O'): + level = eval(args[i][2]) + self.apply_opt_level(level) + for i in range(len(args)): + if args[i] == '-s': + declare = re.sub(r'([\w\d]+)\s*=\s*(.+)', r'self.attrs["\1"]=\2;', args[i+1]) + exec declare + + # Transforms the Settings information into emcc-compatible args (-s X=Y, etc.). Basically + # the reverse of load_settings, except for -Ox which is relevant there but not here + @classmethod + def serialize(self): + ret = [] + for key, value in self.attrs.iteritems(): + if key == key.upper(): # this is a hack. all of our settings are ALL_CAPS, python internals are not + jsoned = json.dumps(value, sort_keys=True) + ret += ['-s', key + '=' + jsoned] + return ret + + @classmethod + def apply_opt_level(self, opt_level, noisy=False): + if opt_level >= 1: + self.attrs['ASM_JS'] = 1 + self.attrs['ASSERTIONS'] = 0 + self.attrs['DISABLE_EXCEPTION_CATCHING'] = 1 + self.attrs['EMIT_GENERATED_FUNCTIONS'] = 1 + if opt_level >= 2: + self.attrs['RELOOP'] = 1 + self.attrs['ALIASING_FUNCTION_POINTERS'] = 1 + if opt_level >= 3: + # Aside from these, -O3 also runs closure compiler and llvm lto + self.attrs['FORCE_ALIGNED_MEMORY'] = 1 + self.attrs['DOUBLE_MODE'] = 0 + self.attrs['PRECISE_I64_MATH'] = 0 + if noisy: logging.warning('Applying some potentially unsafe optimizations! (Use -O2 if this fails.)') + + def __getattr__(self, attr): + if attr in self.attrs: + return self.attrs[attr] + else: + raise AttributeError + + def __setattr__(self, attr, value): + self.attrs[attr] = value + + __instance = None + + @staticmethod + def instance(): + if Settings2.__instance is None: + Settings2.__instance = Settings2.__impl() + return Settings2.__instance + + def __getattr__(self, attr): + return getattr(self.instance(), attr) + + def __setattr__(self, attr, value): + return setattr(self.instance(), attr, value) + +class Settings(object): + __metaclass__ = Settings2 # Building From 846fb98f0ac29dc556c80b606ce7e6e9c1c8afae Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Mon, 12 Aug 2013 10:14:17 -0700 Subject: [PATCH 041/112] tool to find functions with lots of vars --- tools/find_bigvars.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 tools/find_bigvars.py diff --git a/tools/find_bigvars.py b/tools/find_bigvars.py new file mode 100644 index 0000000000000..6bee5dd44a4b9 --- /dev/null +++ b/tools/find_bigvars.py @@ -0,0 +1,24 @@ +''' +Simple tool to find functions with lots of vars. +''' + +import os, sys, re + +filename = sys.argv[1] +i = 0 +curr = None +data = [] +size = 0 +for line in open(filename): + i += 1 + if line.startswith('function '): + size = len(line.split(',')) # params + curr = line + elif line.strip().startswith('var '): + size += len(line.split(',')) + 1 # vars + elif line.startswith('}') and curr: + data.append([curr, size]) + curr = None +data.sort(lambda x, y: x[1] - y[1]) +print ''.join(['%6d : %s' % (x[1], x[0]) for x in data]) + From def22b8d534168bef164f958028bcb17d3ad23aa Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Mon, 12 Aug 2013 11:23:09 -0700 Subject: [PATCH 042/112] legalize add etc. of < 32-bit illegal values properly; fixes #1505 --- src/analyzer.js | 10 +++++++++- tests/cases/phi24_ta2.ll | 3 ++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/analyzer.js b/src/analyzer.js index 1a75230574e71..931ce42112182 100644 --- a/src/analyzer.js +++ b/src/analyzer.js @@ -765,7 +765,15 @@ function analyzer(data, sidePass) { } break; } - case 'add': case 'sub': case 'sdiv': case 'udiv': case 'mul': case 'urem': case 'srem': + case 'add': case 'sub': case 'sdiv': case 'udiv': case 'mul': case 'urem': case 'srem': { + if (sourceBits < 32) { + // when we add illegal types like i24, we must work on the singleton chunks + item.assignTo += '$0'; + item.params[0].ident += '$0'; + item.params[1].ident += '$0'; + } + // fall through + } case 'uitofp': case 'sitofp': case 'fptosi': case 'fptoui': { // We cannot do these in parallel chunks of 32-bit operations. We will handle these in processMathop i++; diff --git a/tests/cases/phi24_ta2.ll b/tests/cases/phi24_ta2.ll index b5b0664bb5e2f..4894d5e6b9f95 100644 --- a/tests/cases/phi24_ta2.ll +++ b/tests/cases/phi24_ta2.ll @@ -549,7 +549,8 @@ safe_mod_func_uint32_t_u_u.exit.i.i: ; preds = %189, %.preheader..p %p_5.sroa.0.0.extract.trunc2674116.i.i = phi i8 [ %p_5.sroa.0.0.extract.trunc2670.i.i, %189 ], [ -1, %.preheader..preheader.split_crit_edge.i.i ] %p_5.sroa.1.sroa.0.0.load6982115.i.i = phi i24 [ %p_5.sroa.1.sroa.0.0.load6978.i.i, %189 ], [ -1, %.preheader..preheader.split_crit_edge.i.i ] store i16 0, i16* @g_84, align 2 - %p_5.sroa.1.1.insert.ext36.i.i = trunc i24 %p_5.sroa.1.sroa.0.0.load6982115.i.i to i16 + %adddd = add i24 %p_5.sroa.1.sroa.0.0.load6982115.i.i, 1 ; test i24 add + %p_5.sroa.1.1.insert.ext36.i.i = trunc i24 %adddd to i16 %p_5.sroa.1.1.insert.shift37.i.i = shl i16 %p_5.sroa.1.1.insert.ext36.i.i, 8 %p_5.sroa.0.0.insert.ext10.i.i = zext i8 %p_5.sroa.0.0.extract.trunc2674116.i.i to i16 %p_5.sroa.0.0.insert.insert12.i.i = or i16 %p_5.sroa.1.1.insert.shift37.i.i, %p_5.sroa.0.0.insert.ext10.i.i From 7caa4278ef41b0c0f8192d8b5ce94f1addd068f8 Mon Sep 17 00:00:00 2001 From: Anthony Pesch Date: Mon, 12 Aug 2013 12:47:47 -0700 Subject: [PATCH 043/112] moved OpenAL helper functions to AL object --- src/library_openal.js | 263 +++++++++++++++++++++--------------------- 1 file changed, 130 insertions(+), 133 deletions(-) diff --git a/src/library_openal.js b/src/library_openal.js index c55415b8cb610..1be5e372251dd 100644 --- a/src/library_openal.js +++ b/src/library_openal.js @@ -6,7 +6,128 @@ var LibraryOpenAL = { contexts: [], currentContext: null, QUEUE_INTERVAL: 25, - QUEUE_LOOKAHEAD: 100 + QUEUE_LOOKAHEAD: 100, + + updateSources: function(context) { + for (var i = 0; i < context.src.length; i++) { + AL.updateSource(context.src[i]); + } + }, + + updateSource: function(src) { +#if OPENAL_DEBUG + var idx = AL.currentContext.src.indexOf(src); +#endif + if (src.state !== 0x1012 /* AL_PLAYING */) { + return; + } + + var currentTime = AL.currentContext.ctx.currentTime; + var startTime = src.bufferPosition; + + for (var i = src.buffersPlayed; i < src.queue.length; i++) { + var entry = src.queue[i]; + + var startOffset = startTime - currentTime; + var endTime = startTime + entry.buffer.duration; + + // Clean up old buffers. + if (currentTime >= endTime) { + // Update our location in the queue. + src.bufferPosition = endTime; + src.buffersPlayed = i + 1; + + // Stop / restart the source when we hit the end. + if (src.buffersPlayed >= src.queue.length) { + if (src.loop) { + AL.setSourceState(src, 0x1012 /* AL_PLAYING */); + } else { + AL.setSourceState(src, 0x1014 /* AL_STOPPED */); + } + } + } + // Process all buffers that'll be played before the next tick. + else if (startOffset < (AL.QUEUE_LOOKAHEAD / 1000) && !entry.src) { + // If the start offset is negative, we need to offset the actual buffer. + var offset = Math.abs(Math.min(startOffset, 0)); + + entry.src = AL.currentContext.ctx.createBufferSource(); + entry.src.buffer = entry.buffer; + entry.src.connect(src.gain); + entry.src.start(startTime, offset); + +#if OPENAL_DEBUG + console.log('updateSource queuing buffer ' + i + ' for source ' + idx + ' at ' + startTime + ' (offset by ' + offset + ')'); +#endif + } + + startTime = endTime; + } + }, + + setSourceState: function(src, state) { +#if OPENAL_DEBUG + var idx = AL.currentContext.src.indexOf(src); +#endif + if (state === 0x1012 /* AL_PLAYING */) { + if (src.state !== 0x1013 /* AL_PAUSED */) { + src.state = 0x1012 /* AL_PLAYING */; + // Reset our position. + src.bufferPosition = AL.currentContext.ctx.currentTime; + src.buffersPlayed = 0; +#if OPENAL_DEBUG + console.log('setSourceState resetting and playing source ' + idx); +#endif + } else { + src.state = 0x1012 /* AL_PLAYING */; + // Use the current offset from src.bufferPosition to resume at the correct point. + src.bufferPosition = AL.currentContext.ctx.currentTime - src.bufferPosition; +#if OPENAL_DEBUG + console.log('setSourceState resuming source ' + idx + ' at ' + src.bufferPosition.toFixed(4)); +#endif + } + AL.stopSourceQueue(src); + AL.updateSource(src); + } else if (state === 0x1013 /* AL_PAUSED */) { + if (src.state === 0x1012 /* AL_PLAYING */) { + src.state = 0x1013 /* AL_PAUSED */; + // Store off the current offset to restore with on resume. + src.bufferPosition = AL.currentContext.ctx.currentTime - src.bufferPosition; + AL.stopSourceQueue(src); +#if OPENAL_DEBUG + console.log('setSourceState pausing source ' + idx + ' at ' + src.bufferPosition.toFixed(4)); +#endif + } + } else if (state === 0x1014 /* AL_STOPPED */) { + if (src.state !== 0x1011 /* AL_INITIAL */) { + src.state = 0x1014 /* AL_STOPPED */; + src.buffersPlayed = src.queue.length; + AL.stopSourceQueue(src); +#if OPENAL_DEBUG + console.log('setSourceState stopping source ' + idx); +#endif + } + } else if (state == 0x1011 /* AL_INITIAL */) { + if (src.state !== 0x1011 /* AL_INITIAL */) { + src.state = 0x1011 /* AL_INITIAL */; + src.bufferPosition = 0; + src.buffersPlayed = 0; +#if OPENAL_DEBUG + console.log('setSourceState initializing source ' + idx); +#endif + } + } + }, + + stopSourceQueue: function(src) { + for (var i = 0; i < src.queue.length; i++) { + var entry = src.queue[i]; + if (entry.src) { + entry.src.stop(0); + entry.src = null; + } + } + } }, alcProcessContext: function(context) {}, @@ -85,7 +206,7 @@ var LibraryOpenAL = { err: 0, src: [], buf: [], - interval: setInterval(function() { _updateSources(context); }, AL.QUEUE_INTERVAL) + interval: setInterval(function() { AL.updateSources(context); }, AL.QUEUE_INTERVAL) }; AL.contexts.push(context); return AL.contexts.length; @@ -94,130 +215,6 @@ var LibraryOpenAL = { } }, - updateSources__deps: ['updateSource'], - updateSources: function(context) { - for (var i = 0; i < context.src.length; i++) { - _updateSource(context.src[i]); - } - }, - - updateSource__deps: ['setSourceState'], - updateSource: function(src) { -#if OPENAL_DEBUG - var idx = AL.currentContext.src.indexOf(src); -#endif - if (src.state !== 0x1012 /* AL_PLAYING */) { - return; - } - - var currentTime = AL.currentContext.ctx.currentTime; - var startTime = src.bufferPosition; - - for (var i = src.buffersPlayed; i < src.queue.length; i++) { - var entry = src.queue[i]; - - var startOffset = startTime - currentTime; - var endTime = startTime + entry.buffer.duration; - - // Clean up old buffers. - if (currentTime >= endTime) { - // Update our location in the queue. - src.bufferPosition = endTime; - src.buffersPlayed = i + 1; - - // Stop / restart the source when we hit the end. - if (src.buffersPlayed >= src.queue.length) { - if (src.loop) { - _setSourceState(src, 0x1012 /* AL_PLAYING */); - } else { - _setSourceState(src, 0x1014 /* AL_STOPPED */); - } - } - } - // Process all buffers that'll be played before the next tick. - else if (startOffset < (AL.QUEUE_LOOKAHEAD / 1000) && !entry.src) { - // If the start offset is negative, we need to offset the actual buffer. - var offset = Math.abs(Math.min(startOffset, 0)); - - entry.src = AL.currentContext.ctx.createBufferSource(); - entry.src.buffer = entry.buffer; - entry.src.connect(src.gain); - entry.src.start(startTime, offset); - -#if OPENAL_DEBUG - console.log('updateSource queuing buffer ' + i + ' for source ' + idx + ' at ' + startTime + ' (offset by ' + offset + ')'); -#endif - } - - startTime = endTime; - } - }, - - setSourceState__deps: ['updateSource', 'stopSourceQueue'], - setSourceState: function(src, state) { -#if OPENAL_DEBUG - var idx = AL.currentContext.src.indexOf(src); -#endif - if (state === 0x1012 /* AL_PLAYING */) { - if (src.state !== 0x1013 /* AL_PAUSED */) { - src.state = 0x1012 /* AL_PLAYING */; - // Reset our position. - src.bufferPosition = AL.currentContext.ctx.currentTime; - src.buffersPlayed = 0; -#if OPENAL_DEBUG - console.log('setSourceState resetting and playing source ' + idx); -#endif - } else { - src.state = 0x1012 /* AL_PLAYING */; - // Use the current offset from src.bufferPosition to resume at the correct point. - src.bufferPosition = AL.currentContext.ctx.currentTime - src.bufferPosition; -#if OPENAL_DEBUG - console.log('setSourceState resuming source ' + idx + ' at ' + src.bufferPosition.toFixed(4)); -#endif - } - _stopSourceQueue(src); - _updateSource(src); - } else if (state === 0x1013 /* AL_PAUSED */) { - if (src.state === 0x1012 /* AL_PLAYING */) { - src.state = 0x1013 /* AL_PAUSED */; - // Store off the current offset to restore with on resume. - src.bufferPosition = AL.currentContext.ctx.currentTime - src.bufferPosition; - _stopSourceQueue(src); -#if OPENAL_DEBUG - console.log('setSourceState pausing source ' + idx + ' at ' + src.bufferPosition.toFixed(4)); -#endif - } - } else if (state === 0x1014 /* AL_STOPPED */) { - if (src.state !== 0x1011 /* AL_INITIAL */) { - src.state = 0x1014 /* AL_STOPPED */; - src.buffersPlayed = src.queue.length; - _stopSourceQueue(src); -#if OPENAL_DEBUG - console.log('setSourceState stopping source ' + idx); -#endif - } - } else if (state == 0x1011 /* AL_INITIAL */) { - if (src.state !== 0x1011 /* AL_INITIAL */) { - src.state = 0x1011 /* AL_INITIAL */; - src.bufferPosition = 0; - src.buffersPlayed = 0; -#if OPENAL_DEBUG - console.log('setSourceState initializing source ' + idx); -#endif - } - } - }, - - stopSourceQueue: function(src) { - for (var i = 0; i < src.queue.length; i++) { - var entry = src.queue[i]; - if (entry.src) { - entry.src.stop(0); - entry.src = null; - } - } - }, - alGetError: function() { if (!AL.currentContext) { return 0xA004 /* AL_INVALID_OPERATION */; @@ -336,7 +333,7 @@ var LibraryOpenAL = { } else { src.queue = [{ buffer: buffer }]; } - _updateSource(src); + AL.updateSource(src); break; case 0x202 /* AL_SOURCE_RELATIVE */: if (value === 1 /* AL_TRUE */) { @@ -505,7 +502,7 @@ var LibraryOpenAL = { src.queue.push({ buffer: buffer, src: null }); } - _updateSource(src); + AL.updateSource(src); }, alSourceUnqueueBuffers__deps: ["updateSource"], @@ -544,7 +541,7 @@ var LibraryOpenAL = { src.buffersPlayed--; } - _updateSource(src); + AL.updateSource(src); }, alDeleteBuffers: function(count, buffers) @@ -687,7 +684,7 @@ var LibraryOpenAL = { AL.currentContext.err = 0xA001 /* AL_INVALID_NAME */; return; } - _setSourceState(src, 0x1012 /* AL_PLAYING */); + AL.setSourceState(src, 0x1012 /* AL_PLAYING */); }, alSourceStop__deps: ['setSourceState'], @@ -707,7 +704,7 @@ var LibraryOpenAL = { AL.currentContext.err = 0xA001 /* AL_INVALID_NAME */; return; } - _setSourceState(src, 0x1014 /* AL_STOPPED */); + AL.setSourceState(src, 0x1014 /* AL_STOPPED */); }, alSourcePause__deps: ['setSourceState'], @@ -727,7 +724,7 @@ var LibraryOpenAL = { AL.currentContext.err = 0xA001 /* AL_INVALID_NAME */; return; } - _setSourceState(src, 0x1013 /* AL_PAUSED */); + AL.setSourceState(src, 0x1013 /* AL_PAUSED */); }, alGetSourcei__deps: ['updateSource'], @@ -754,7 +751,7 @@ var LibraryOpenAL = { // so we also forcefully update the source when alGetSourcei is queried // to aid in the common scenario of application calling alGetSourcei(AL_BUFFERS_PROCESSED) // to recycle buffers. - _updateSource(src); + AL.updateSource(src); switch (param) { case 0x202 /* AL_SOURCE_RELATIVE */: From b77b7482bf0570efa9b8f319ccb591fbab2d5f89 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Mon, 12 Aug 2013 14:15:58 -0700 Subject: [PATCH 044/112] split out handling SDL events from making C events for them, to make SDL_PumpEvents work; fixes #1419 --- src/library_sdl.js | 76 +++++++++++++++++++++++++++--------------- tests/runner.py | 13 ++++++++ tests/sdl_pumpevents.c | 54 ++++++++++++++++++++++++++++++ 3 files changed, 116 insertions(+), 27 deletions(-) create mode 100644 tests/sdl_pumpevents.c diff --git a/src/library_sdl.js b/src/library_sdl.js index 24f3de1bdcfbe..92cfc7e508a34 100644 --- a/src/library_sdl.js +++ b/src/library_sdl.js @@ -519,6 +519,46 @@ var LibrarySDL = { return; }, + handleEvent: function(event) { + if (event.handled) return; + event.handled = true; + + switch (event.type) { + case 'keydown': case 'keyup': { + var down = event.type === 'keydown'; + var code = SDL.keyCodes[event.keyCode] || event.keyCode; + + {{{ makeSetValue('SDL.keyboardState', 'code', 'down', 'i8') }}}; + // TODO: lmeta, rmeta, numlock, capslock, KMOD_MODE, KMOD_RESERVED + SDL.modState = ({{{ makeGetValue('SDL.keyboardState', '1248', 'i8') }}} ? 0x0040 | 0x0080 : 0) | // KMOD_LCTRL & KMOD_RCTRL + ({{{ makeGetValue('SDL.keyboardState', '1249', 'i8') }}} ? 0x0001 | 0x0002 : 0) | // KMOD_LSHIFT & KMOD_RSHIFT + ({{{ makeGetValue('SDL.keyboardState', '1250', 'i8') }}} ? 0x0100 | 0x0200 : 0); // KMOD_LALT & KMOD_RALT + + if (down) { + SDL.keyboardMap[code] = event.keyCode; // save the DOM input, which we can use to unpress it during blur + } else { + delete SDL.keyboardMap[code]; + } + + break; + } + case 'mousedown': case 'mouseup': + if (event.type == 'mousedown') { + // SDL_BUTTON(x) is defined as (1 << ((x)-1)). SDL buttons are 1-3, + // and DOM buttons are 0-2, so this means that the below formula is + // correct. + SDL.buttonState |= 1 << event.button; + } else if (event.type == 'mouseup') { + SDL.buttonState &= ~(1 << event.button); + } + // fall through + case 'mousemove': { + Browser.calculateMouseEvent(event); + break; + } + } + }, + makeCEvent: function(event, ptr) { if (typeof event === 'number') { // This is a pointer to a native C event that was SDL_PushEvent'ed @@ -526,7 +566,9 @@ var LibrarySDL = { return; } - switch(event.type) { + SDL.handleEvent(event); + + switch (event.type) { case 'keydown': case 'keyup': { var down = event.type === 'keydown'; //Module.print('Received key event: ' + event.keyCode); @@ -543,19 +585,6 @@ var LibrarySDL = { scan = SDL.scanCodes[key] || key; } - var code = SDL.keyCodes[event.keyCode] || event.keyCode; - {{{ makeSetValue('SDL.keyboardState', 'code', 'down', 'i8') }}}; - if (down) { - SDL.keyboardMap[code] = event.keyCode; // save the DOM input, which we can use to unpress it during blur - } else { - delete SDL.keyboardMap[code]; - } - - // TODO: lmeta, rmeta, numlock, capslock, KMOD_MODE, KMOD_RESERVED - SDL.modState = ({{{ makeGetValue('SDL.keyboardState', '1248', 'i8') }}} ? 0x0040 | 0x0080 : 0) | // KMOD_LCTRL & KMOD_RCTRL - ({{{ makeGetValue('SDL.keyboardState', '1249', 'i8') }}} ? 0x0001 | 0x0002 : 0) | // KMOD_LSHIFT & KMOD_RSHIFT - ({{{ makeGetValue('SDL.keyboardState', '1250', 'i8') }}} ? 0x0100 | 0x0200 : 0); // KMOD_LALT & KMOD_RALT - {{{ makeSetValue('ptr', 'SDL.structs.KeyboardEvent.type', 'SDL.DOMEventToSDLEvent[event.type]', 'i32') }}} {{{ makeSetValue('ptr', 'SDL.structs.KeyboardEvent.state', 'down ? 1 : 0', 'i8') }}} {{{ makeSetValue('ptr', 'SDL.structs.KeyboardEvent.repeat', '0', 'i8') }}} // TODO @@ -575,18 +604,7 @@ var LibrarySDL = { } break; } - case 'mousedown': case 'mouseup': - if (event.type == 'mousedown') { - // SDL_BUTTON(x) is defined as (1 << ((x)-1)). SDL buttons are 1-3, - // and DOM buttons are 0-2, so this means that the below formula is - // correct. - SDL.buttonState |= 1 << event.button; - } else if (event.type == 'mouseup') { - SDL.buttonState &= ~(1 << event.button); - } - // fall through - case 'mousemove': { - Browser.calculateMouseEvent(event); + case 'mousedown': case 'mouseup': case 'mousemove': { if (event.type != 'mousemove') { var down = event.type === 'mousedown'; {{{ makeSetValue('ptr', 'SDL.structs.MouseButtonEvent.type', 'SDL.DOMEventToSDLEvent[event.type]', 'i32') }}}; @@ -1174,7 +1192,11 @@ var LibrarySDL = { } }, - SDL_PumpEvents: function(){}, + SDL_PumpEvents: function(){ + SDL.events.forEach(function(event) { + SDL.handleEvent(event); + }); + }, SDL_SetColors: function(surf, colors, firstColor, nColors) { var surfData = SDL.surfaces[surf]; diff --git a/tests/runner.py b/tests/runner.py index 9a8670b623898..8d2cbcb4c957a 100755 --- a/tests/runner.py +++ b/tests/runner.py @@ -13471,6 +13471,19 @@ def test_sdl_mouse_offsets(self): Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'sdl_mouse.c'), '-O2', '--minify', '0', '-o', 'sdl_mouse.js', '--pre-js', 'pre.js']).communicate() self.run_browser('page.html', '', '/report_result?600') + def test_sdl_pumpevents(self): + # key events should be detected using SDL_PumpEvents + open(os.path.join(self.get_dir(), 'pre.js'), 'w').write(''' + function keydown(c) { + var event = document.createEvent("KeyboardEvent"); + event.initKeyEvent("keydown", true, true, window, + 0, 0, 0, 0, + c, c); + document.dispatchEvent(event); + } + ''') + self.btest('sdl_pumpevents.c', expected='3', args=['--pre-js', 'pre.js']) + def test_sdl_audio(self): shutil.copyfile(path_from_root('tests', 'sounds', 'alarmvictory_1.ogg'), os.path.join(self.get_dir(), 'sound.ogg')) shutil.copyfile(path_from_root('tests', 'sounds', 'alarmcreatemiltaryfoot_1.wav'), os.path.join(self.get_dir(), 'sound2.wav')) diff --git a/tests/sdl_pumpevents.c b/tests/sdl_pumpevents.c new file mode 100644 index 0000000000000..64becaade60ad --- /dev/null +++ b/tests/sdl_pumpevents.c @@ -0,0 +1,54 @@ +#include +#include +#include + +#include +// bug - SDL_GetKeyboardState doesn't return scancodes, it returns keycodes, so acts exactly like +// SDL_GetKeyState instead +#define SDL_GetKeyState SDL_GetKeyboardState + +int result = 0; + +int loop1() +{ + unsigned i; + int r = 0; + + // method 1: SDL_PollEvent loop + SDL_Event e; + while (SDL_PollEvent(&e)); + + const Uint8 *keys = SDL_GetKeyState(NULL); + if (keys[SDLK_LEFT]) + r = 1; + + return r; +} + +int loop2() +{ + unsigned i; + int r = 0; + + // method 2: SDL_PumpEvents + SDL_PumpEvents(); + + const Uint8 *keys = SDL_GetKeyState(NULL); + if (keys[SDLK_RIGHT]) + r = 2; + + return r; +} + +int main(int argc, char *argv[]) +{ + SDL_Init(SDL_INIT_EVERYTHING); + SDL_SetVideoMode(600, 400, 32, SDL_SWSURFACE); + + emscripten_run_script("keydown(37);"); // left + result += loop1(); + emscripten_run_script("keydown(39);"); // right + result += loop2(); + REPORT_RESULT(); + return 0; +} From 85afe6e007dad36772914d18451f4d67cbf20549 Mon Sep 17 00:00:00 2001 From: Anthony Pesch Date: Mon, 12 Aug 2013 15:05:35 -0700 Subject: [PATCH 045/112] Move JS_ENGINE_OVERRIDE to shared Building object to avoid sys.modules hack --- tests/runner.py | 8 +------- tests/test_benchmark.py | 2 +- tools/shared.py | 1 + 3 files changed, 3 insertions(+), 8 deletions(-) diff --git a/tests/runner.py b/tests/runner.py index b9b9244531516..8dbad2e34cf5b 100755 --- a/tests/runner.py +++ b/tests/runner.py @@ -38,7 +38,6 @@ def path_from_root(*pathelems): checked_sanity = False test_modes = ['default', 'o1', 'o2', 'asm1', 'asm2', 'asm2g', 'asm2x86', 's_0_0', 's_0_1'] test_index = 0 -js_engine_override = None class RunnerCore(unittest.TestCase): emcc_args = None @@ -656,11 +655,6 @@ def btest(self, filename, expected=None, reference=None, force_c=False, referenc ################################################################################################### if __name__ == '__main__': - # The runner module is imported by the tests, add the current module - # instance into the cache so global variables such as js_engine_override - # are shared correctly with the test suite modules. - sys.modules['runner'] = sys.modules[__name__] - # Sanity checks total_engines = len(JS_ENGINES) JS_ENGINES = filter(check_engine, JS_ENGINES) @@ -745,7 +739,7 @@ def btest(self, filename, expected=None, reference=None, force_c=False, referenc arg = sys.argv[i] if arg.isupper(): print 'Interpreting all capital argument "%s" as JS_ENGINE override' % arg - js_engine_override = eval(arg) + Building.JS_ENGINE_OVERRIDE = eval(arg) sys.argv[i] = None sys.argv = filter(lambda arg: arg is not None, sys.argv) diff --git a/tests/test_benchmark.py b/tests/test_benchmark.py index 6efc1f638c891..e9cfee52df082 100644 --- a/tests/test_benchmark.py +++ b/tests/test_benchmark.py @@ -56,7 +56,7 @@ def setUpClass(self): # Pick the JS engine to benchmark. If you specify one, it will be picked. For example, python tests/runner.py benchmark SPIDERMONKEY_ENGINE global JS_ENGINE - JS_ENGINE = runner.js_engine_override if runner.js_engine_override is not None else JS_ENGINES[0] + JS_ENGINE = Building.JS_ENGINE_OVERRIDE if Building.JS_ENGINE_OVERRIDE is not None else JS_ENGINES[0] print 'Benchmarking JS engine: %s' % JS_ENGINE def print_stats(self, times, native_times, last=False, reps=TEST_REPS): diff --git a/tools/shared.py b/tools/shared.py index b8a796acddaea..917f548ed660d 100644 --- a/tools/shared.py +++ b/tools/shared.py @@ -764,6 +764,7 @@ class Building: COMPILER = CLANG LLVM_OPTS = False COMPILER_TEST_OPTS = [] # For use of the test runner + JS_ENGINE_OVERRIDE = None # Used to pass the JS engine override from runner.py -> test_benchmark.py @staticmethod def get_building_env(native=False): From 2b2950a658133f80b1ca4d14279b7adfd7aaa42b Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Mon, 12 Aug 2013 15:09:05 -0700 Subject: [PATCH 046/112] temporarily remove test_sdl_pumpevents --- tests/runner.py | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/tests/runner.py b/tests/runner.py index 8d2cbcb4c957a..9a8670b623898 100755 --- a/tests/runner.py +++ b/tests/runner.py @@ -13471,19 +13471,6 @@ def test_sdl_mouse_offsets(self): Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'sdl_mouse.c'), '-O2', '--minify', '0', '-o', 'sdl_mouse.js', '--pre-js', 'pre.js']).communicate() self.run_browser('page.html', '', '/report_result?600') - def test_sdl_pumpevents(self): - # key events should be detected using SDL_PumpEvents - open(os.path.join(self.get_dir(), 'pre.js'), 'w').write(''' - function keydown(c) { - var event = document.createEvent("KeyboardEvent"); - event.initKeyEvent("keydown", true, true, window, - 0, 0, 0, 0, - c, c); - document.dispatchEvent(event); - } - ''') - self.btest('sdl_pumpevents.c', expected='3', args=['--pre-js', 'pre.js']) - def test_sdl_audio(self): shutil.copyfile(path_from_root('tests', 'sounds', 'alarmvictory_1.ogg'), os.path.join(self.get_dir(), 'sound.ogg')) shutil.copyfile(path_from_root('tests', 'sounds', 'alarmcreatemiltaryfoot_1.wav'), os.path.join(self.get_dir(), 'sound2.wav')) From 8ceb235681031c306b410a147db44088bad0fe1b Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Mon, 12 Aug 2013 15:10:06 -0700 Subject: [PATCH 047/112] re-add test_sdl_pumpevents --- tests/test_browser.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/test_browser.py b/tests/test_browser.py index a24a2de4bc725..60c030aadcd84 100644 --- a/tests/test_browser.py +++ b/tests/test_browser.py @@ -787,6 +787,19 @@ def test_sdl_mouse_offsets(self): Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'sdl_mouse.c'), '-O2', '--minify', '0', '-o', 'sdl_mouse.js', '--pre-js', 'pre.js']).communicate() self.run_browser('page.html', '', '/report_result?600') + def test_sdl_pumpevents(self): + # key events should be detected using SDL_PumpEvents + open(os.path.join(self.get_dir(), 'pre.js'), 'w').write(''' + function keydown(c) { + var event = document.createEvent("KeyboardEvent"); + event.initKeyEvent("keydown", true, true, window, + 0, 0, 0, 0, + c, c); + document.dispatchEvent(event); + } + ''') + self.btest('sdl_pumpevents.c', expected='3', args=['--pre-js', 'pre.js']) + def test_sdl_audio(self): shutil.copyfile(path_from_root('tests', 'sounds', 'alarmvictory_1.ogg'), os.path.join(self.get_dir(), 'sound.ogg')) shutil.copyfile(path_from_root('tests', 'sounds', 'alarmcreatemiltaryfoot_1.wav'), os.path.join(self.get_dir(), 'sound2.wav')) From 61c35703dc8fdb8ab0f5b5a1a527349907b6b199 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Mon, 12 Aug 2013 17:33:59 -0700 Subject: [PATCH 048/112] add explicit stack trace to FS errors --- src/library_fs.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/library_fs.js b/src/library_fs.js index 167b69527c0bb..9c83fcad48f05 100644 --- a/src/library_fs.js +++ b/src/library_fs.js @@ -35,7 +35,7 @@ mergeInto(LibraryManager.library, { break; } } - this.message = ERRNO_MESSAGES[errno]; + this.message = ERRNO_MESSAGES[errno] + ' : ' + new Error().stack; }, handleFSError: function(e) { @@ -1364,4 +1364,5 @@ mergeInto(LibraryManager.library, { return stream.stream_ops.mmap(stream, buffer, offset, length, position, prot, flags); } } -}); \ No newline at end of file +}); + From 3090cdd713609a9d3f749d13002a898e03cbd5e3 Mon Sep 17 00:00:00 2001 From: Anthony Pesch Date: Mon, 12 Aug 2013 18:36:21 -0700 Subject: [PATCH 049/112] fixed test skip support --- tests/runner.py | 48 ++++++++++++++++++++++++++---------------------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/tests/runner.py b/tests/runner.py index 8dbad2e34cf5b..41fd5b90110e1 100755 --- a/tests/runner.py +++ b/tests/runner.py @@ -48,9 +48,6 @@ class RunnerCore(unittest.TestCase): env = {} - def skipme(self): # used by tests we ask on the commandline to be skipped, see right before call to unittest.main - return self.skip('requested to be skipped') - def setUp(self): Settings.reset() self.banned_js_engines = [] @@ -715,25 +712,6 @@ def btest(self, filename, expected=None, reference=None, force_c=False, referenc print 'Running all test modes on test "%s"' % test sys.argv = [sys.argv[0]] + map(lambda mode: mode+'.'+test, test_modes) - # Skip requested tests - for i in range(len(sys.argv)): - arg = sys.argv[i] - if arg.startswith('skip:'): - which = arg.split('skip:')[1] - if which.startswith('ALL.'): - ignore, test = which.split('.') - which = map(lambda mode: mode+'.'+test, test_modes) - else: - which = [which] - - print >> sys.stderr, ','.join(which) - for test in which: - print >> sys.stderr, 'will skip "%s"' % test - exec(test + ' = RunnerCore.skipme') - - sys.argv[i] = '' - sys.argv = filter(lambda arg: arg, sys.argv) - # Extract the JS engine override from the arguments (used by benchmarks) for i in range(1, len(sys.argv)): arg = sys.argv[i] @@ -751,6 +729,32 @@ def btest(self, filename, expected=None, reference=None, force_c=False, referenc __import__(module_name) modules.append(sys.modules[module_name]) + # Skip requested tests + def skipme(self): + return self.skip('requested to be skipped') + + for i in range(len(sys.argv)): + arg = sys.argv[i] + if arg.startswith('skip:'): + which = arg.split('skip:')[1] + if which.startswith('ALL.'): + ignore, test = which.split('.') + which = map(lambda mode: mode+'.'+test, test_modes) + else: + which = [which] + + print >> sys.stderr, ','.join(which) + for test in which: + print >> sys.stderr, 'will skip "%s"' % test + for m in modules: + try: + exec('m.' + test + ' = skipme') + break + except: + pass + sys.argv[i] = None + sys.argv = filter(lambda arg: arg is not None, sys.argv) + # Filter and load tests from the discovered modules loader = unittest.TestLoader() names = sys.argv[1:] From bd615f6f4499cc2b928c5603ba98d6bd689e608e Mon Sep 17 00:00:00 2001 From: ToadKing Date: Mon, 12 Aug 2013 23:45:41 -0400 Subject: [PATCH 050/112] reimplement getCurrentTime --- src/library_openal.js | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/library_openal.js b/src/library_openal.js index cbb5c0bb1c633..7f5d5df667d39 100644 --- a/src/library_openal.js +++ b/src/library_openal.js @@ -8,6 +8,12 @@ var LibraryOpenAL = { QUEUE_INTERVAL: 25, QUEUE_LOOKAHEAD: 100, + getCurrentTime: function(context) { + // currentTime is frozen during execution, use performance timers to + // emulate the current time at call time + return (window['performance']['now']() - context.startTime) / 1000.0; + }, + updateSources: function(context) { for (var i = 0; i < context.src.length; i++) { AL.updateSource(context.src[i]); @@ -22,7 +28,7 @@ var LibraryOpenAL = { return; } - var currentTime = AL.currentContext.ctx.currentTime; + var currentTime = AL.getCurrentTime(AL.currentContext); var startTime = src.bufferPosition; for (var i = src.buffersPlayed; i < src.queue.length; i++) { @@ -73,7 +79,7 @@ var LibraryOpenAL = { if (src.state !== 0x1013 /* AL_PAUSED */) { src.state = 0x1012 /* AL_PLAYING */; // Reset our position. - src.bufferPosition = AL.currentContext.ctx.currentTime; + src.bufferPosition = AL.getCurrentTime(AL.currentContext); src.buffersPlayed = 0; #if OPENAL_DEBUG console.log('setSourceState resetting and playing source ' + idx); @@ -81,7 +87,7 @@ var LibraryOpenAL = { } else { src.state = 0x1012 /* AL_PLAYING */; // Use the current offset from src.bufferPosition to resume at the correct point. - src.bufferPosition = AL.currentContext.ctx.currentTime - src.bufferPosition; + src.bufferPosition = AL.getCurrentTime(AL.currentContext) - src.bufferPosition; #if OPENAL_DEBUG console.log('setSourceState resuming source ' + idx + ' at ' + src.bufferPosition.toFixed(4)); #endif @@ -92,7 +98,7 @@ var LibraryOpenAL = { if (src.state === 0x1012 /* AL_PLAYING */) { src.state = 0x1013 /* AL_PAUSED */; // Store off the current offset to restore with on resume. - src.bufferPosition = AL.currentContext.ctx.currentTime - src.bufferPosition; + src.bufferPosition = AL.getCurrentTime(AL.currentContext) - src.bufferPosition; AL.stopSourceQueue(src); #if OPENAL_DEBUG console.log('setSourceState pausing source ' + idx + ' at ' + src.bufferPosition.toFixed(4)); @@ -206,7 +212,8 @@ var LibraryOpenAL = { err: 0, src: [], buf: [], - interval: setInterval(function() { AL.updateSources(context); }, AL.QUEUE_INTERVAL) + interval: setInterval(function() { AL.updateSources(context); }, AL.QUEUE_INTERVAL), + startTime: window['performance']['now']() }; AL.contexts.push(context); return AL.contexts.length; From b639ed59378df54fc0513fb00ac97d0678f8ee6e Mon Sep 17 00:00:00 2001 From: Anthony Pesch Date: Tue, 13 Aug 2013 09:25:13 -0700 Subject: [PATCH 051/112] shuffled around command line processing to fix skipping --- tests/runner.py | 98 ++++++++++++++++++++++++------------------------- 1 file changed, 49 insertions(+), 49 deletions(-) diff --git a/tests/runner.py b/tests/runner.py index 41fd5b90110e1..2f508dfc6404f 100755 --- a/tests/runner.py +++ b/tests/runner.py @@ -48,6 +48,9 @@ class RunnerCore(unittest.TestCase): env = {} + def skipme(self): # used by tests we ask on the commandline to be skipped, see right before call to unittest.main + return self.skip('requested to be skipped') + def setUp(self): Settings.reset() self.banned_js_engines = [] @@ -660,9 +663,55 @@ def btest(self, filename, expected=None, reference=None, force_c=False, referenc elif len(JS_ENGINES) < total_engines: print 'WARNING: Not all the JS engines in JS_ENGINES appears to work, ignoring those.' + # Create a list of modules to load tests from + modules = [] + for filename in glob.glob(os.path.join(os.path.dirname(__file__), 'test*.py')): + module_dir, module_file = os.path.split(filename) + module_name, module_ext = os.path.splitext(module_file) + __import__(module_name) + modules.append(sys.modules[module_name]) + + # Extract the JS engine override from the arguments (used by benchmarks) + for i in range(1, len(sys.argv)): + arg = sys.argv[i] + if arg.isupper(): + print 'Interpreting all capital argument "%s" as JS_ENGINE override' % arg + Building.JS_ENGINE_OVERRIDE = eval(arg) + sys.argv[i] = None + sys.argv = filter(lambda arg: arg is not None, sys.argv) + # If an argument comes in as test_*, treat it as a test of the default suite sys.argv = map(lambda arg: arg if not arg.startswith('test_') else 'default.' + arg, sys.argv) + # If a test (e.g. test_html) is specified as ALL.test_html, add an entry for each test_mode + if len(sys.argv) == 2 and sys.argv[1].startswith('ALL.'): + ignore, test = sys.argv[1].split('.') + print 'Running all test modes on test "%s"' % test + sys.argv = [sys.argv[0]] + map(lambda mode: mode+'.'+test, test_modes) + + # Skip requested tests + for i in range(len(sys.argv)): + arg = sys.argv[i] + if arg.startswith('skip:'): + which = arg.split('skip:')[1] + if which.startswith('ALL.'): + ignore, test = which.split('.') + which = map(lambda mode: mode+'.'+test, test_modes) + else: + which = [which] + + print >> sys.stderr, ','.join(which) + for test in which: + print >> sys.stderr, 'will skip "%s"' % test + for m in modules: + try: + exec('m.' + test + ' = RunnerCore("skipme")') + break + except: + pass + sys.argv[i] = None + sys.argv = filter(lambda arg: arg is not None, sys.argv) + # If no tests were specified, run the core suite if len(sys.argv) == 1: sys.argv = [sys.argv[0]] + map(lambda mode: mode, test_modes) @@ -706,55 +755,6 @@ def btest(self, filename, expected=None, reference=None, force_c=False, referenc ''' time.sleep(2) - # If a test (e.g. test_html) is specified as ALL.test_html, add an entry for each test_mode - if len(sys.argv) == 2 and sys.argv[1].startswith('ALL.'): - ignore, test = sys.argv[1].split('.') - print 'Running all test modes on test "%s"' % test - sys.argv = [sys.argv[0]] + map(lambda mode: mode+'.'+test, test_modes) - - # Extract the JS engine override from the arguments (used by benchmarks) - for i in range(1, len(sys.argv)): - arg = sys.argv[i] - if arg.isupper(): - print 'Interpreting all capital argument "%s" as JS_ENGINE override' % arg - Building.JS_ENGINE_OVERRIDE = eval(arg) - sys.argv[i] = None - sys.argv = filter(lambda arg: arg is not None, sys.argv) - - # Create a list of modules to load tests from - modules = [] - for filename in glob.glob(os.path.join(os.path.dirname(__file__), 'test*.py')): - module_dir, module_file = os.path.split(filename) - module_name, module_ext = os.path.splitext(module_file) - __import__(module_name) - modules.append(sys.modules[module_name]) - - # Skip requested tests - def skipme(self): - return self.skip('requested to be skipped') - - for i in range(len(sys.argv)): - arg = sys.argv[i] - if arg.startswith('skip:'): - which = arg.split('skip:')[1] - if which.startswith('ALL.'): - ignore, test = which.split('.') - which = map(lambda mode: mode+'.'+test, test_modes) - else: - which = [which] - - print >> sys.stderr, ','.join(which) - for test in which: - print >> sys.stderr, 'will skip "%s"' % test - for m in modules: - try: - exec('m.' + test + ' = skipme') - break - except: - pass - sys.argv[i] = None - sys.argv = filter(lambda arg: arg is not None, sys.argv) - # Filter and load tests from the discovered modules loader = unittest.TestLoader() names = sys.argv[1:] From 984e7b2aff83c41a8272058ab4716bfc222ff58e Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 13 Aug 2013 11:27:13 -0700 Subject: [PATCH 052/112] properly parse things like { i8*, { i64, i8* } (i8*)* }, issue #1319 --- src/analyzer.js | 16 +++++++++++++++- tests/cases/phi24_ta2.ll | 1 + 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/analyzer.js b/src/analyzer.js index 931ce42112182..2a7d64f5867e6 100644 --- a/src/analyzer.js +++ b/src/analyzer.js @@ -953,9 +953,23 @@ function analyzer(data, sidePass) { if (type[0] == '{' || type[0] == '<') { type = nonPointing; var packed = type[0] == '<'; + var internal = type; + if (packed) { + if (internal[internal.length-1] != '>') { + warnOnce('ignoring type ' + internal); + return; // function pointer or such + } + internal = internal.substr(1, internal.length-2); + } + assert(internal[0] == '{', internal); + if (internal[internal.length-1] != '}') { + warnOnce('ignoring type ' + internal); + return; // function pointer or such + } + internal = internal.substr(2, internal.length-4); Types.types[type] = { name_: type, - fields: splitTokenList(tokenize(type.substr(2 + packed, type.length - 4 - 2*packed)).tokens).map(function(segment) { + fields: splitTokenList(tokenize(internal).tokens).map(function(segment) { return segment[0].text; }), packed: packed, diff --git a/tests/cases/phi24_ta2.ll b/tests/cases/phi24_ta2.ll index 4894d5e6b9f95..2d9b664629893 100644 --- a/tests/cases/phi24_ta2.ll +++ b/tests/cases/phi24_ta2.ll @@ -252,6 +252,7 @@ target triple = "i386-pc-linux-gnu" @g_287 = internal constant i32 -1, align 4 define i32 @main(i32 %argc, i8** nocapture %argv) nounwind { + %msgdummy = alloca { i8*, { i64, i8* } (i8*)* } ; test for parsing of this kind of stuff, compilation-only test %p_6.i.i = alloca %union.U3, align 8 %1 = icmp eq i32 %argc, 2 br i1 %1, label %2, label %7 From fae19ff8567d874f7da6a614516ebd0f21de8e8c Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 13 Aug 2013 11:49:02 -0700 Subject: [PATCH 053/112] warn if triple is not what we expect --- src/intertyper.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/intertyper.js b/src/intertyper.js index 3fc840c4dc29e..31e97bd0a3ada 100644 --- a/src/intertyper.js +++ b/src/intertyper.js @@ -365,8 +365,17 @@ function intertyper(data, sidePass, baseLineNums) { } if (tokensLength >= 3 && (token0Text == 'call' || token1Text == 'call')) return 'Call'; - if (token0Text == 'target') + if (token0Text == 'target') { + if (token1Text == 'triple') { + var triple = item.tokens[3].text; + triple = triple.substr(1, triple.length-2); + var expected = TARGET_LE32 ? 'le32-unknown-nacl' : 'i386-pc-linux-gnu'; + if (triple !== expected) { + warn('using an unexpected LLVM triple: ' + [triple, ' !== ', expected] + ' (are you using emcc for everything and not clang?)'); + } + } return '/dev/null'; + } if (token0Text == ';') return '/dev/null'; if (tokensLength >= 3 && token0Text == 'invoke') From ce965a4e68540009cfe9d047a55cfe54f6e5605d Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 13 Aug 2013 14:32:58 -0700 Subject: [PATCH 054/112] add some slack to glgears tests --- tests/test_browser.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_browser.py b/tests/test_browser.py index 60c030aadcd84..be2c388b5d155 100644 --- a/tests/test_browser.py +++ b/tests/test_browser.py @@ -1056,7 +1056,7 @@ def do_GET(s): server.terminate() def test_glgears(self): - self.btest('hello_world_gles.c', reference='gears.png', + self.btest('hello_world_gles.c', reference='gears.png', reference_slack=1, args=['-DHAVE_BUILTIN_SINCOS'], outfile='something.html', message='You should see animating gears.') @@ -1079,7 +1079,7 @@ def test_fulles2_sdlproc(self): self.btest('full_es2_sdlproc.c', '1', args=['-s', 'GL_TESTING=1', '-DHAVE_BUILTIN_SINCOS', '-s', 'FULL_ES2=1']) def test_glgears_deriv(self): - self.btest('hello_world_gles_deriv.c', reference='gears.png', + self.btest('hello_world_gles_deriv.c', reference='gears.png', reference_slack=1, args=['-DHAVE_BUILTIN_SINCOS'], outfile='something.html', message='You should see animating gears.') with open('something.html') as f: From df16c302a90998f4daf1b9f2e71ef0e8e1b26dda Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 13 Aug 2013 16:00:17 -0700 Subject: [PATCH 055/112] exit outlining when we are done, as early as possible --- tools/js-optimizer.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js index ff6aee670a599..f15b2fe128304 100644 --- a/tools/js-optimizer.js +++ b/tools/js-optimizer.js @@ -3475,6 +3475,9 @@ function outline(ast) { } } } + function done() { + return asmData.splitCounter >= asmData.maxOutlinings || measureSize(func) <= extraInfo.sizeToOutline; + } while (1) { i--; calcMinIndex(); // TODO: optimize @@ -3530,7 +3533,7 @@ function outline(ast) { if (ret.length > pre) { // we outlined recursively, reset our state here //printErr('successful outline in recursion ' + func[1] + ' due to recursive in level ' + level); - if (measureSize(func) <= extraInfo.sizeToOutline) break; + if (done()) break; end = i-1; sizeSeen = 0; canRestart = true; @@ -3570,7 +3573,7 @@ function outline(ast) { if (newFuncs.length) { ret.push.apply(ret, newFuncs); } - if (measureSize(func) <= extraInfo.sizeToOutline) break; + if (done()) break; sizeSeen = 0; end = i-1; canRestart = true; From 7759fd47927791c6f2927036f9536787493a7efd Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 13 Aug 2013 17:33:02 -0700 Subject: [PATCH 056/112] fix limit in outlining.flatten, and adjust to 1/4 of the outlining target --- tools/js-optimizer.js | 2 +- .../test-js-optimizer-asm-outline1-output.js | 159 +++- .../test-js-optimizer-asm-outline2-output.js | 790 ++++++++++-------- 3 files changed, 552 insertions(+), 399 deletions(-) diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js index f15b2fe128304..7d72b7b863f76 100644 --- a/tools/js-optimizer.js +++ b/tools/js-optimizer.js @@ -3001,7 +3001,7 @@ function outline(ast) { // Try to flatten out code as much as possible, to make outlining more feasible. function flatten(func, asmData) { - var minSize = sizeToOutline; + var minSize = extraInfo.sizeToOutline/4; var helperId = 0; function getHelper() { while (1) { diff --git a/tools/test-js-optimizer-asm-outline1-output.js b/tools/test-js-optimizer-asm-outline1-output.js index 904e56cf0c3c3..d8ea9446ebf80 100644 --- a/tools/test-js-optimizer-asm-outline1-output.js +++ b/tools/test-js-optimizer-asm-outline1-output.js @@ -276,8 +276,29 @@ function vars_w_stack(x, y) { function chain() { var helper$0 = 0, sp = 0; sp = STACKTOP; - STACKTOP = STACKTOP + 240 | 0; + STACKTOP = STACKTOP + 464 | 0; helper$0 = 1; + if (helper$0) { + helper$0 = 0; + if (x == 1) { + print(1); + } else { + helper$0 = 1; + } + } + if (helper$0) { + helper$0 = 0; + if (x == 2) { + print(2); + } else { + helper$0 = 1; + } + } + HEAP32[sp + 8 >> 2] = helper$0; + HEAP32[sp + 48 >> 2] = 0; + HEAP32[sp + 52 >> 2] = 0; + chain$4(sp); + helper$0 = HEAP32[sp + 8 >> 2] | 0; HEAP32[sp + 8 >> 2] = helper$0; HEAP32[sp + 40 >> 2] = 0; HEAP32[sp + 44 >> 2] = 0; @@ -288,6 +309,16 @@ function chain() { HEAP32[sp + 36 >> 2] = 0; chain$2(sp); helper$0 = HEAP32[sp + 8 >> 2] | 0; + HEAP32[sp + 8 >> 2] = helper$0; + HEAP32[sp + 24 >> 2] = 0; + HEAP32[sp + 28 >> 2] = 0; + chain$1(sp); + helper$0 = HEAP32[sp + 8 >> 2] | 0; + HEAP32[sp + 8 >> 2] = helper$0; + HEAP32[sp + 16 >> 2] = 0; + HEAP32[sp + 20 >> 2] = 0; + chain$0(sp); + helper$0 = HEAP32[sp + 8 >> 2] | 0; STACKTOP = sp; } function lin$0(sp) { @@ -500,22 +531,21 @@ function chain$0(sp) { sp = sp | 0; var helper$0 = 0; helper$0 = HEAP32[sp + 8 >> 2] | 0; - if (x == 7) { - print(7); - } else if (x == 8) { - print(8); - } else if (x == 9) { - print(9); - } else if (x == 10) { - print(10); - } else if (x == 11) { - print(11); - } else if (x == 12) { - print(12); - } else if (1) { - print(99); - } else { - helper$0 = 1; + if (helper$0) { + helper$0 = 0; + if (x == 12) { + print(12); + } else { + helper$0 = 1; + } + } + if (helper$0) { + helper$0 = 0; + if (1) { + print(99); + } else { + helper$0 = 1; + } } HEAP32[sp + 8 >> 2] = helper$0; } @@ -523,20 +553,21 @@ function chain$1(sp) { sp = sp | 0; var helper$0 = 0; helper$0 = HEAP32[sp + 8 >> 2] | 0; - if (x == 1) { - print(1); - } else if (x == 2) { - print(2); - } else if (x == 3) { - print(3); - } else if (x == 4) { - print(4); - } else if (x == 5) { - print(5); - } else if (x == 6) { - print(6); - } else { - helper$0 = 1; + if (helper$0) { + helper$0 = 0; + if (x == 10) { + print(10); + } else { + helper$0 = 1; + } + } + if (helper$0) { + helper$0 = 0; + if (x == 11) { + print(11); + } else { + helper$0 = 1; + } } HEAP32[sp + 8 >> 2] = helper$0; } @@ -546,11 +577,19 @@ function chain$2(sp) { helper$0 = HEAP32[sp + 8 >> 2] | 0; if (helper$0) { helper$0 = 0; - HEAP32[sp + 8 >> 2] = helper$0; - HEAP32[sp + 16 >> 2] = 0; - HEAP32[sp + 20 >> 2] = 0; - chain$0(sp); - helper$0 = HEAP32[sp + 8 >> 2] | 0; + if (x == 8) { + print(8); + } else { + helper$0 = 1; + } + } + if (helper$0) { + helper$0 = 0; + if (x == 9) { + print(9); + } else { + helper$0 = 1; + } } HEAP32[sp + 8 >> 2] = helper$0; } @@ -560,11 +599,49 @@ function chain$3(sp) { helper$0 = HEAP32[sp + 8 >> 2] | 0; if (helper$0) { helper$0 = 0; - HEAP32[sp + 8 >> 2] = helper$0; - HEAP32[sp + 24 >> 2] = 0; - HEAP32[sp + 28 >> 2] = 0; - chain$1(sp); - helper$0 = HEAP32[sp + 8 >> 2] | 0; + if (x == 6) { + print(6); + } else { + helper$0 = 1; + } + } + if (helper$0) { + helper$0 = 0; + if (x == 7) { + print(7); + } else { + helper$0 = 1; + } + } + HEAP32[sp + 8 >> 2] = helper$0; +} +function chain$4(sp) { + sp = sp | 0; + var helper$0 = 0; + helper$0 = HEAP32[sp + 8 >> 2] | 0; + if (helper$0) { + helper$0 = 0; + if (x == 3) { + print(3); + } else { + helper$0 = 1; + } + } + if (helper$0) { + helper$0 = 0; + if (x == 4) { + print(4); + } else { + helper$0 = 1; + } + } + if (helper$0) { + helper$0 = 0; + if (x == 5) { + print(5); + } else { + helper$0 = 1; + } } HEAP32[sp + 8 >> 2] = helper$0; } diff --git a/tools/test-js-optimizer-asm-outline2-output.js b/tools/test-js-optimizer-asm-outline2-output.js index 28feb6cb26316..2658fda05c62b 100644 --- a/tools/test-js-optimizer-asm-outline2-output.js +++ b/tools/test-js-optimizer-asm-outline2-output.js @@ -23,7 +23,7 @@ function linear() { } function _free($mem) { $mem = $mem | 0; - var $5 = 0, $10 = 0, $16 = 0, $21 = 0, $25 = 0, $26 = 0, $psize_0 = 0, $p_0 = 0, $189 = 0, $194 = 0, sp = 0; + var $5 = 0, $10 = 0, $16 = 0, $21 = 0, $25 = 0, $26 = 0, $psize_0 = 0, $p_0 = 0, $189 = 0, $194 = 0, sp = 0, helper$0 = 0; sp = STACKTOP; if (($mem | 0) == 0) { STACKTOP = sp; @@ -39,59 +39,71 @@ function _free($mem) { } $16 = $mem + (($10 & -8) - 8) | 0; L621 : do { - if (($10 & 1 | 0) == 0) { - $21 = HEAP32[($mem - 8 | 0) >> 2] | 0; - if (($10 & 3 | 0) == 0) { - return; - } - $25 = $mem + (-8 - $21 | 0) | 0; - $26 = $21 + ($10 & -8) | 0; - if (($mem + (-8 - $21 | 0) | 0) >>> 0 < $5 >>> 0) { - _abort(); - } - if (($25 | 0) == (HEAP32[25] | 0)) { - if ((HEAP32[($mem + (($10 & -8) - 4) | 0) >> 2] & 3 | 0) != 3) { - $p_0 = $25; - $psize_0 = $26; + helper$0 = 1; + if (helper$0) { + helper$0 = 0; + if (($10 & 1 | 0) == 0) { + $21 = HEAP32[($mem - 8 | 0) >> 2] | 0; + if (($10 & 3 | 0) == 0) { + return; + } + $25 = $mem + (-8 - $21 | 0) | 0; + $26 = $21 + ($10 & -8) | 0; + if (($mem + (-8 - $21 | 0) | 0) >>> 0 < $5 >>> 0) { + _abort(); + } + if (($25 | 0) == (HEAP32[25] | 0)) { + if ((HEAP32[($mem + (($10 & -8) - 4) | 0) >> 2] & 3 | 0) != 3) { + $p_0 = $25; + $psize_0 = $26; + break; + } + HEAP32[22] = $26; + HEAP32[($mem + (($10 & -8) - 4) | 0) >> 2] = HEAP32[($mem + (($10 & -8) - 4) | 0) >> 2] & -2; + HEAP32[$mem + ((-8 - $21 | 0) + 4) >> 2] = $26 | 1; + HEAP32[($mem + (($10 & -8) - 8) | 0) >> 2] = $26; + return; + } + HEAP32[sp + 8 >> 2] = $mem; + HEAP32[sp + 16 >> 2] = $5; + HEAP32[sp + 40 >> 2] = $21; + HEAP32[sp + 48 >> 2] = $25; + HEAP32[sp + 56 >> 2] = $26; + HEAP32[sp + 216 >> 2] = $psize_0; + HEAP32[sp + 224 >> 2] = $p_0; + HEAP32[sp + 696 >> 2] = 0; + HEAP32[sp + 700 >> 2] = 0; + _free$1(sp); + $psize_0 = HEAP32[sp + 216 >> 2] | 0; + $p_0 = HEAP32[sp + 224 >> 2] | 0; + tempValue = HEAP32[sp + 696 >> 2] | 0; + tempInt = HEAP32[sp + 700 >> 2] | 0; + tempDouble = +HEAPF32[sp + 700 >> 2]; + HEAP32[sp + 696 >> 2] = 0; + HEAP32[sp + 700 >> 2] = 0; + if ((tempValue | 0) == 1) { break; } - HEAP32[22] = $26; - HEAP32[($mem + (($10 & -8) - 4) | 0) >> 2] = HEAP32[($mem + (($10 & -8) - 4) | 0) >> 2] & -2; - HEAP32[$mem + ((-8 - $21 | 0) + 4) >> 2] = $26 | 1; - HEAP32[($mem + (($10 & -8) - 8) | 0) >> 2] = $26; - return; - } - HEAP32[sp + 8 >> 2] = $mem; - HEAP32[sp + 16 >> 2] = $5; - HEAP32[sp + 40 >> 2] = $21; - HEAP32[sp + 48 >> 2] = $25; - HEAP32[sp + 56 >> 2] = $26; - HEAP32[sp + 216 >> 2] = $psize_0; - HEAP32[sp + 224 >> 2] = $p_0; - HEAP32[sp + 648 >> 2] = 0; - HEAP32[sp + 652 >> 2] = 0; - _free$1(sp); - $psize_0 = HEAP32[sp + 216 >> 2] | 0; - $p_0 = HEAP32[sp + 224 >> 2] | 0; - tempValue = HEAP32[sp + 648 >> 2] | 0; - tempInt = HEAP32[sp + 652 >> 2] | 0; - tempDouble = +HEAPF32[sp + 652 >> 2]; - HEAP32[sp + 648 >> 2] = 0; - HEAP32[sp + 652 >> 2] = 0; - if ((tempValue | 0) == 1) { - break; - } - if ((tempValue | 0) == 2) { - switch (tempInt | 0) { - case 2: - { - break L621; + if ((tempValue | 0) == 2) { + switch (tempInt | 0) { + case 2: + { + break L621; + } } } + } else { + helper$0 = 1; + } + } + if (helper$0) { + helper$0 = 0; + if (1) { + $p_0 = $mem - 8 | 0; + $psize_0 = $10 & -8; + } else { + helper$0 = 1; } - } else { - $p_0 = $mem - 8 | 0; - $psize_0 = $10 & -8; } } while (0); $189 = $p_0; @@ -109,14 +121,14 @@ function _free($mem) { HEAP32[sp + 224 >> 2] = $p_0; HEAP32[sp + 232 >> 2] = $189; HEAP32[sp + 240 >> 2] = $194; - HEAP32[sp + 656 >> 2] = 0; - HEAP32[sp + 660 >> 2] = 0; + HEAP32[sp + 704 >> 2] = 0; + HEAP32[sp + 708 >> 2] = 0; _free$2(sp); - tempValue = HEAP32[sp + 656 >> 2] | 0; - tempInt = HEAP32[sp + 660 >> 2] | 0; - tempDouble = +HEAPF32[sp + 660 >> 2]; - HEAP32[sp + 656 >> 2] = 0; - HEAP32[sp + 660 >> 2] = 0; + tempValue = HEAP32[sp + 704 >> 2] | 0; + tempInt = HEAP32[sp + 708 >> 2] | 0; + tempDouble = +HEAPF32[sp + 708 >> 2]; + HEAP32[sp + 704 >> 2] = 0; + HEAP32[sp + 708 >> 2] = 0; if ((tempValue | 0) == 5) { return; } @@ -153,175 +165,200 @@ function linear$1(sp) { } function _free$0(sp) { sp = sp | 0; - var $194 = 0, $233 = 0, $mem = 0, $10 = 0, $236 = 0, $16 = 0, $_pre_phi305 = 0, $267 = 0, $270 = 0, $273 = 0, $294 = 0, $299 = 0, $R7_1 = 0, $R7_0 = 0, $RP9_0 = 0, $301 = 0, $302 = 0, $305 = 0, $306 = 0, $278 = 0, $320 = 0, $351 = 0, $364 = 0; + var helper$3 = 0, $194 = 0, $233 = 0, $mem = 0, $10 = 0, $236 = 0, $16 = 0, $_pre_phi305 = 0, $267 = 0, $270 = 0, $273 = 0, helper$4 = 0, $294 = 0, $299 = 0, $R7_1 = 0, $R7_0 = 0, $RP9_0 = 0, $301 = 0, $302 = 0, $305 = 0, $306 = 0, $278 = 0, $320 = 0, $351 = 0, $364 = 0; $mem = HEAP32[sp + 8 >> 2] | 0; $10 = HEAP32[sp + 24 >> 2] | 0; $16 = HEAP32[sp + 32 >> 2] | 0; $194 = HEAP32[sp + 240 >> 2] | 0; + helper$3 = HEAP32[sp + 664 >> 2] | 0; OL : do { - if ($194 >>> 0 < 256) { - $233 = HEAP32[$mem + ($10 & -8) >> 2] | 0; - $236 = HEAP32[$mem + ($10 & -8 | 4) >> 2] | 0; - do { - if (($233 | 0) != (120 + ($194 >>> 3 << 1 << 2) | 0 | 0)) { - if ($233 >>> 0 < (HEAP32[24] | 0) >>> 0) { + if (helper$3) { + helper$3 = 0; + if ($194 >>> 0 < 256) { + $233 = HEAP32[$mem + ($10 & -8) >> 2] | 0; + $236 = HEAP32[$mem + ($10 & -8 | 4) >> 2] | 0; + do { + if (($233 | 0) != (120 + ($194 >>> 3 << 1 << 2) | 0 | 0)) { + if ($233 >>> 0 < (HEAP32[24] | 0) >>> 0) { + _abort(); + } + if ((HEAP32[$233 + 12 >> 2] | 0) == ($16 | 0)) { + break; + } _abort(); } - if ((HEAP32[$233 + 12 >> 2] | 0) == ($16 | 0)) { - break; - } - _abort(); + } while (0); + if (($236 | 0) == ($233 | 0)) { + HEAP32[20] = HEAP32[20] & (1 << ($194 >>> 3) ^ -1); + HEAP32[sp + 688 >> 2] = 1; + break OL; } - } while (0); - if (($236 | 0) == ($233 | 0)) { - HEAP32[20] = HEAP32[20] & (1 << ($194 >>> 3) ^ -1); - HEAP32[sp + 640 >> 2] = 1; - break OL; - } - do { - if (($236 | 0) == (120 + ($194 >>> 3 << 1 << 2) | 0 | 0)) { - $_pre_phi305 = $236 + 8 | 0; - } else { - if ($236 >>> 0 < (HEAP32[24] | 0) >>> 0) { - _abort(); - } - if ((HEAP32[($236 + 8 | 0) >> 2] | 0) == ($16 | 0)) { + do { + if (($236 | 0) == (120 + ($194 >>> 3 << 1 << 2) | 0 | 0)) { $_pre_phi305 = $236 + 8 | 0; - break; - } - _abort(); - } - } while (0); - HEAP32[$233 + 12 >> 2] = $236; - HEAP32[$_pre_phi305 >> 2] = $233; - } else { - $267 = $mem + (($10 & -8) - 8) | 0; - $270 = HEAP32[$mem + (($10 & -8) + 16) >> 2] | 0; - $273 = HEAP32[$mem + ($10 & -8 | 4) >> 2] | 0; - do { - if (($273 | 0) == ($267 | 0)) { - $294 = HEAP32[($mem + (($10 & -8) + 12) | 0) >> 2] | 0; - if (($294 | 0) == 0) { - $299 = HEAP32[($mem + (($10 & -8) + 8) | 0) >> 2] | 0; - if (($299 | 0) == 0) { - $R7_1 = 0; - break; - } else { - $R7_0 = $299; - $RP9_0 = $mem + (($10 & -8) + 8) | 0; - } } else { - $R7_0 = $294; - $RP9_0 = $mem + (($10 & -8) + 12) | 0; - } - while (1) { - $301 = $R7_0 + 20 | 0; - $302 = HEAP32[$301 >> 2] | 0; - if (($302 | 0) != 0) { - $R7_0 = $302; - $RP9_0 = $301; - continue; + if ($236 >>> 0 < (HEAP32[24] | 0) >>> 0) { + _abort(); } - $305 = $R7_0 + 16 | 0; - $306 = HEAP32[$305 >> 2] | 0; - if (($306 | 0) == 0) { + if ((HEAP32[($236 + 8 | 0) >> 2] | 0) == ($16 | 0)) { + $_pre_phi305 = $236 + 8 | 0; break; - } else { - $R7_0 = $306; - $RP9_0 = $305; } - } - if ($RP9_0 >>> 0 < (HEAP32[24] | 0) >>> 0) { _abort(); - } else { - HEAP32[$RP9_0 >> 2] = 0; - $R7_1 = $R7_0; - break; } - } else { - $278 = HEAP32[$mem + ($10 & -8) >> 2] | 0; - if ($278 >>> 0 < (HEAP32[24] | 0) >>> 0) { - _abort(); + } while (0); + HEAP32[$233 + 12 >> 2] = $236; + HEAP32[$_pre_phi305 >> 2] = $233; + } else { + helper$3 = 1; + } + } + if (helper$3) { + helper$3 = 0; + if (1) { + $267 = $mem + (($10 & -8) - 8) | 0; + $270 = HEAP32[$mem + (($10 & -8) + 16) >> 2] | 0; + $273 = HEAP32[$mem + ($10 & -8 | 4) >> 2] | 0; + do { + helper$4 = 1; + if (helper$4) { + helper$4 = 0; + if (($273 | 0) == ($267 | 0)) { + $294 = HEAP32[($mem + (($10 & -8) + 12) | 0) >> 2] | 0; + if (($294 | 0) == 0) { + $299 = HEAP32[($mem + (($10 & -8) + 8) | 0) >> 2] | 0; + if (($299 | 0) == 0) { + $R7_1 = 0; + break; + } else { + $R7_0 = $299; + $RP9_0 = $mem + (($10 & -8) + 8) | 0; + } + } else { + $R7_0 = $294; + $RP9_0 = $mem + (($10 & -8) + 12) | 0; + } + while (1) { + $301 = $R7_0 + 20 | 0; + $302 = HEAP32[$301 >> 2] | 0; + if (($302 | 0) != 0) { + $R7_0 = $302; + $RP9_0 = $301; + continue; + } + $305 = $R7_0 + 16 | 0; + $306 = HEAP32[$305 >> 2] | 0; + if (($306 | 0) == 0) { + break; + } else { + $R7_0 = $306; + $RP9_0 = $305; + } + } + if ($RP9_0 >>> 0 < (HEAP32[24] | 0) >>> 0) { + _abort(); + } else { + HEAP32[$RP9_0 >> 2] = 0; + $R7_1 = $R7_0; + break; + } + } else { + helper$4 = 1; + } } - if ((HEAP32[($278 + 12 | 0) >> 2] | 0) != ($267 | 0)) { - _abort(); + if (helper$4) { + helper$4 = 0; + if (1) { + $278 = HEAP32[$mem + ($10 & -8) >> 2] | 0; + if ($278 >>> 0 < (HEAP32[24] | 0) >>> 0) { + _abort(); + } + if ((HEAP32[($278 + 12 | 0) >> 2] | 0) != ($267 | 0)) { + _abort(); + } + if ((HEAP32[($273 + 8 | 0) >> 2] | 0) == ($267 | 0)) { + HEAP32[($278 + 12 | 0) >> 2] = $273; + HEAP32[($273 + 8 | 0) >> 2] = $278; + $R7_1 = $273; + break; + } else { + _abort(); + } + } else { + helper$4 = 1; + } } - if ((HEAP32[($273 + 8 | 0) >> 2] | 0) == ($267 | 0)) { - HEAP32[($278 + 12 | 0) >> 2] = $273; - HEAP32[($273 + 8 | 0) >> 2] = $278; - $R7_1 = $273; - break; + } while (0); + if (($270 | 0) == 0) { + HEAP32[sp + 688 >> 2] = 1; + break OL; + } + $320 = 384 + (HEAP32[($mem + (($10 & -8) + 20) | 0) >> 2] << 2) | 0; + do { + if (($267 | 0) == (HEAP32[$320 >> 2] | 0)) { + HEAP32[$320 >> 2] = $R7_1; + if (($R7_1 | 0) != 0) { + break; + } + HEAP32[21] = HEAP32[21] & (1 << HEAP32[($mem + (($10 & -8) + 20) | 0) >> 2] ^ -1); + HEAP32[sp + 688 >> 2] = 2; + HEAP32[sp + 692 >> 2] = 2; + break OL; } else { - _abort(); + if ($270 >>> 0 < (HEAP32[24] | 0) >>> 0) { + _abort(); + } + if ((HEAP32[($270 + 16 | 0) >> 2] | 0) == ($267 | 0)) { + HEAP32[($270 + 16 | 0) >> 2] = $R7_1; + } else { + HEAP32[$270 + 20 >> 2] = $R7_1; + } + if (($R7_1 | 0) == 0) { + HEAP32[sp + 688 >> 2] = 2; + HEAP32[sp + 692 >> 2] = 2; + break OL; + } } + } while (0); + if ($R7_1 >>> 0 < (HEAP32[24] | 0) >>> 0) { + _abort(); } - } while (0); - if (($270 | 0) == 0) { - HEAP32[sp + 640 >> 2] = 1; - break OL; - } - $320 = 384 + (HEAP32[($mem + (($10 & -8) + 20) | 0) >> 2] << 2) | 0; - do { - if (($267 | 0) == (HEAP32[$320 >> 2] | 0)) { - HEAP32[$320 >> 2] = $R7_1; - if (($R7_1 | 0) != 0) { - break; + HEAP32[$R7_1 + 24 >> 2] = $270; + $351 = HEAP32[$mem + (($10 & -8) + 8) >> 2] | 0; + do { + if (($351 | 0) != 0) { + if ($351 >>> 0 < (HEAP32[24] | 0) >>> 0) { + _abort(); + } else { + HEAP32[$R7_1 + 16 >> 2] = $351; + HEAP32[$351 + 24 >> 2] = $R7_1; + break; + } } - HEAP32[21] = HEAP32[21] & (1 << HEAP32[($mem + (($10 & -8) + 20) | 0) >> 2] ^ -1); - HEAP32[sp + 640 >> 2] = 2; - HEAP32[sp + 644 >> 2] = 2; + } while (0); + $364 = HEAP32[$mem + (($10 & -8) + 12) >> 2] | 0; + if (($364 | 0) == 0) { + HEAP32[sp + 688 >> 2] = 1; break OL; - } else { - if ($270 >>> 0 < (HEAP32[24] | 0) >>> 0) { - _abort(); - } - if ((HEAP32[($270 + 16 | 0) >> 2] | 0) == ($267 | 0)) { - HEAP32[($270 + 16 | 0) >> 2] = $R7_1; - } else { - HEAP32[$270 + 20 >> 2] = $R7_1; - } - if (($R7_1 | 0) == 0) { - HEAP32[sp + 640 >> 2] = 2; - HEAP32[sp + 644 >> 2] = 2; - break OL; - } } - } while (0); - if ($R7_1 >>> 0 < (HEAP32[24] | 0) >>> 0) { - _abort(); - } - HEAP32[$R7_1 + 24 >> 2] = $270; - $351 = HEAP32[$mem + (($10 & -8) + 8) >> 2] | 0; - do { - if (($351 | 0) != 0) { - if ($351 >>> 0 < (HEAP32[24] | 0) >>> 0) { - _abort(); - } else { - HEAP32[$R7_1 + 16 >> 2] = $351; - HEAP32[$351 + 24 >> 2] = $R7_1; - break; - } + if ($364 >>> 0 < (HEAP32[24] | 0) >>> 0) { + _abort(); + } else { + HEAP32[$R7_1 + 20 >> 2] = $364; + HEAP32[$364 + 24 >> 2] = $R7_1; + HEAP32[sp + 688 >> 2] = 1; + break OL; } - } while (0); - $364 = HEAP32[$mem + (($10 & -8) + 12) >> 2] | 0; - if (($364 | 0) == 0) { - HEAP32[sp + 640 >> 2] = 1; - break OL; - } - if ($364 >>> 0 < (HEAP32[24] | 0) >>> 0) { - _abort(); } else { - HEAP32[$R7_1 + 20 >> 2] = $364; - HEAP32[$364 + 24 >> 2] = $R7_1; - HEAP32[sp + 640 >> 2] = 1; - break OL; + helper$3 = 1; } } } while (0); + HEAP32[sp + 664 >> 2] = helper$3; } function _free$1(sp) { sp = sp | 0; - var $21 = 0, $37 = 0, $mem = 0, $40 = 0, $5 = 0, $25 = 0, $26 = 0, $_pre_phi307 = 0, $69 = 0, $72 = 0, $75 = 0, $95 = 0, $100 = 0, $R_1 = 0, $R_0 = 0, $RP_0 = 0, $102 = 0, $103 = 0, $106 = 0, $107 = 0, $80 = 0, $120 = 0, $151 = 0, $164 = 0, $p_0 = 0, $psize_0 = 0; + var $21 = 0, $37 = 0, $mem = 0, $40 = 0, $5 = 0, $25 = 0, $26 = 0, $_pre_phi307 = 0, $69 = 0, $72 = 0, $75 = 0, helper$1 = 0, $95 = 0, $100 = 0, $R_1 = 0, $R_0 = 0, $RP_0 = 0, $102 = 0, $103 = 0, $106 = 0, $107 = 0, $80 = 0, $120 = 0, $151 = 0, $164 = 0, $p_0 = 0, $psize_0 = 0; $mem = HEAP32[sp + 8 >> 2] | 0; $5 = HEAP32[sp + 16 >> 2] | 0; $21 = HEAP32[sp + 40 >> 2] | 0; @@ -348,7 +385,7 @@ function _free$1(sp) { HEAP32[20] = HEAP32[20] & (1 << ($21 >>> 3) ^ -1); $p_0 = $25; $psize_0 = $26; - HEAP32[sp + 648 >> 2] = 1; + HEAP32[sp + 696 >> 2] = 1; break OL; } do { @@ -369,74 +406,86 @@ function _free$1(sp) { HEAP32[$_pre_phi307 >> 2] = $37; $p_0 = $25; $psize_0 = $26; - HEAP32[sp + 648 >> 2] = 1; + HEAP32[sp + 696 >> 2] = 1; break OL; } $69 = $mem + (-8 - $21 | 0) | 0; $72 = HEAP32[$mem + ((-8 - $21 | 0) + 24) >> 2] | 0; $75 = HEAP32[$mem + ((-8 - $21 | 0) + 12) >> 2] | 0; do { - if (($75 | 0) == ($69 | 0)) { - $95 = HEAP32[($mem + ((-8 - $21 | 0) + 20) | 0) >> 2] | 0; - if (($95 | 0) == 0) { - $100 = HEAP32[($mem + ((-8 - $21 | 0) + 16) | 0) >> 2] | 0; - if (($100 | 0) == 0) { - $R_1 = 0; - break; + helper$1 = 1; + if (helper$1) { + helper$1 = 0; + if (($75 | 0) == ($69 | 0)) { + $95 = HEAP32[($mem + ((-8 - $21 | 0) + 20) | 0) >> 2] | 0; + if (($95 | 0) == 0) { + $100 = HEAP32[($mem + ((-8 - $21 | 0) + 16) | 0) >> 2] | 0; + if (($100 | 0) == 0) { + $R_1 = 0; + break; + } else { + $R_0 = $100; + $RP_0 = $mem + ((-8 - $21 | 0) + 16) | 0; + } + } else { + $R_0 = $95; + $RP_0 = $mem + ((-8 - $21 | 0) + 20) | 0; + } + while (1) { + $102 = $R_0 + 20 | 0; + $103 = HEAP32[$102 >> 2] | 0; + if (($103 | 0) != 0) { + $R_0 = $103; + $RP_0 = $102; + continue; + } + $106 = $R_0 + 16 | 0; + $107 = HEAP32[$106 >> 2] | 0; + if (($107 | 0) == 0) { + break; + } else { + $R_0 = $107; + $RP_0 = $106; + } + } + if ($RP_0 >>> 0 < $5 >>> 0) { + _abort(); } else { - $R_0 = $100; - $RP_0 = $mem + ((-8 - $21 | 0) + 16) | 0; + HEAP32[$RP_0 >> 2] = 0; + $R_1 = $R_0; + break; } } else { - $R_0 = $95; - $RP_0 = $mem + ((-8 - $21 | 0) + 20) | 0; + helper$1 = 1; } - while (1) { - $102 = $R_0 + 20 | 0; - $103 = HEAP32[$102 >> 2] | 0; - if (($103 | 0) != 0) { - $R_0 = $103; - $RP_0 = $102; - continue; + } + if (helper$1) { + helper$1 = 0; + if (1) { + $80 = HEAP32[$mem + ((-8 - $21 | 0) + 8) >> 2] | 0; + if ($80 >>> 0 < $5 >>> 0) { + _abort(); + } + if ((HEAP32[($80 + 12 | 0) >> 2] | 0) != ($69 | 0)) { + _abort(); } - $106 = $R_0 + 16 | 0; - $107 = HEAP32[$106 >> 2] | 0; - if (($107 | 0) == 0) { + if ((HEAP32[($75 + 8 | 0) >> 2] | 0) == ($69 | 0)) { + HEAP32[($80 + 12 | 0) >> 2] = $75; + HEAP32[($75 + 8 | 0) >> 2] = $80; + $R_1 = $75; break; } else { - $R_0 = $107; - $RP_0 = $106; + _abort(); } - } - if ($RP_0 >>> 0 < $5 >>> 0) { - _abort(); } else { - HEAP32[$RP_0 >> 2] = 0; - $R_1 = $R_0; - break; - } - } else { - $80 = HEAP32[$mem + ((-8 - $21 | 0) + 8) >> 2] | 0; - if ($80 >>> 0 < $5 >>> 0) { - _abort(); - } - if ((HEAP32[($80 + 12 | 0) >> 2] | 0) != ($69 | 0)) { - _abort(); - } - if ((HEAP32[($75 + 8 | 0) >> 2] | 0) == ($69 | 0)) { - HEAP32[($80 + 12 | 0) >> 2] = $75; - HEAP32[($75 + 8 | 0) >> 2] = $80; - $R_1 = $75; - break; - } else { - _abort(); + helper$1 = 1; } } } while (0); if (($72 | 0) == 0) { $p_0 = $25; $psize_0 = $26; - HEAP32[sp + 648 >> 2] = 1; + HEAP32[sp + 696 >> 2] = 1; break OL; } $120 = 384 + (HEAP32[($mem + ((-8 - $21 | 0) + 28) | 0) >> 2] << 2) | 0; @@ -449,8 +498,8 @@ function _free$1(sp) { HEAP32[21] = HEAP32[21] & (1 << HEAP32[($mem + ((-8 - $21 | 0) + 28) | 0) >> 2] ^ -1); $p_0 = $25; $psize_0 = $26; - HEAP32[sp + 648 >> 2] = 2; - HEAP32[sp + 652 >> 2] = 2; + HEAP32[sp + 696 >> 2] = 2; + HEAP32[sp + 700 >> 2] = 2; break OL; } else { if ($72 >>> 0 < (HEAP32[24] | 0) >>> 0) { @@ -464,8 +513,8 @@ function _free$1(sp) { if (($R_1 | 0) == 0) { $p_0 = $25; $psize_0 = $26; - HEAP32[sp + 648 >> 2] = 2; - HEAP32[sp + 652 >> 2] = 2; + HEAP32[sp + 696 >> 2] = 2; + HEAP32[sp + 700 >> 2] = 2; break OL; } } @@ -490,7 +539,7 @@ function _free$1(sp) { if (($164 | 0) == 0) { $p_0 = $25; $psize_0 = $26; - HEAP32[sp + 648 >> 2] = 1; + HEAP32[sp + 696 >> 2] = 1; break OL; } if ($164 >>> 0 < (HEAP32[24] | 0) >>> 0) { @@ -500,7 +549,7 @@ function _free$1(sp) { HEAP32[$164 + 24 >> 2] = $R_1; $p_0 = $25; $psize_0 = $26; - HEAP32[sp + 648 >> 2] = 1; + HEAP32[sp + 696 >> 2] = 1; break OL; } } while (0); @@ -509,7 +558,7 @@ function _free$1(sp) { } function _free$2(sp) { sp = sp | 0; - var $194 = 0, $16 = 0, $204 = 0, $psize_0 = 0, $p_0 = 0, $220 = 0, $189 = 0, $227 = 0, $mem = 0, $10 = 0, $psize_1 = 0, $390 = 0, $396 = 0, $F16_0 = 0, $_pre_phi = 0, $404 = 0, $414 = 0, $415 = 0, $I18_0 = 0, $428 = 0, $436 = 0, $443 = 0, $447 = 0, $448 = 0, $463 = 0, $K19_0 = 0, $T_0 = 0, $472 = 0, $473 = 0, label = 0, $486 = 0, $487 = 0, $489 = 0, $501 = 0, $sp_0_in_i = 0, $sp_0_i = 0; + var helper$2 = 0, $194 = 0, $16 = 0, $204 = 0, $psize_0 = 0, $p_0 = 0, $220 = 0, $189 = 0, $227 = 0, helper$3 = 0, $mem = 0, $10 = 0, $psize_1 = 0, $390 = 0, $396 = 0, $F16_0 = 0, $_pre_phi = 0, $404 = 0, $414 = 0, $415 = 0, $I18_0 = 0, $428 = 0, $436 = 0, $443 = 0, $447 = 0, $448 = 0, helper$5 = 0, $463 = 0, $K19_0 = 0, $T_0 = 0, $472 = 0, $473 = 0, label = 0, $486 = 0, $487 = 0, $489 = 0, $501 = 0, $sp_0_in_i = 0, $sp_0_i = 0; $mem = HEAP32[sp + 8 >> 2] | 0; $10 = HEAP32[sp + 24 >> 2] | 0; $16 = HEAP32[sp + 32 >> 2] | 0; @@ -519,73 +568,88 @@ function _free$2(sp) { $194 = HEAP32[sp + 240 >> 2] | 0; OL : do { do { - if (($194 & 2 | 0) == 0) { - if (($16 | 0) == (HEAP32[26] | 0)) { - $204 = (HEAP32[23] | 0) + $psize_0 | 0; - HEAP32[23] = $204; - HEAP32[26] = $p_0; - HEAP32[$p_0 + 4 >> 2] = $204 | 1; - if (($p_0 | 0) == (HEAP32[25] | 0)) { - HEAP32[25] = 0; - HEAP32[22] = 0; - } - if ($204 >>> 0 <= (HEAP32[27] | 0) >>> 0) { - HEAP32[sp + 656 >> 2] = 5; + helper$2 = 1; + if (helper$2) { + helper$2 = 0; + if (($194 & 2 | 0) == 0) { + if (($16 | 0) == (HEAP32[26] | 0)) { + $204 = (HEAP32[23] | 0) + $psize_0 | 0; + HEAP32[23] = $204; + HEAP32[26] = $p_0; + HEAP32[$p_0 + 4 >> 2] = $204 | 1; + if (($p_0 | 0) == (HEAP32[25] | 0)) { + HEAP32[25] = 0; + HEAP32[22] = 0; + } + if ($204 >>> 0 <= (HEAP32[27] | 0) >>> 0) { + HEAP32[sp + 704 >> 2] = 5; + break OL; + } + _sys_trim(0) | 0; + HEAP32[sp + 704 >> 2] = 5; break OL; } - _sys_trim(0) | 0; - HEAP32[sp + 656 >> 2] = 5; - break OL; - } - if (($16 | 0) == (HEAP32[25] | 0)) { - $220 = (HEAP32[22] | 0) + $psize_0 | 0; - HEAP32[22] = $220; - HEAP32[25] = $p_0; - HEAP32[$p_0 + 4 >> 2] = $220 | 1; - HEAP32[$189 + $220 >> 2] = $220; - HEAP32[sp + 656 >> 2] = 5; - break OL; - } - $227 = ($194 & -8) + $psize_0 | 0; - L726 : do { - HEAP32[sp + 8 >> 2] = $mem; - HEAP32[sp + 24 >> 2] = $10; - HEAP32[sp + 32 >> 2] = $16; - HEAP32[sp + 240 >> 2] = $194; - HEAP32[sp + 640 >> 2] = 0; - HEAP32[sp + 644 >> 2] = 0; - _free$0(sp); - tempValue = HEAP32[sp + 640 >> 2] | 0; - tempInt = HEAP32[sp + 644 >> 2] | 0; - tempDouble = +HEAPF32[sp + 644 >> 2]; - HEAP32[sp + 640 >> 2] = 0; - HEAP32[sp + 644 >> 2] = 0; - if ((tempValue | 0) == 1) { - break; + if (($16 | 0) == (HEAP32[25] | 0)) { + $220 = (HEAP32[22] | 0) + $psize_0 | 0; + HEAP32[22] = $220; + HEAP32[25] = $p_0; + HEAP32[$p_0 + 4 >> 2] = $220 | 1; + HEAP32[$189 + $220 >> 2] = $220; + HEAP32[sp + 704 >> 2] = 5; + break OL; } - if ((tempValue | 0) == 2) { - switch (tempInt | 0) { - case 2: - { - break L726; + $227 = ($194 & -8) + $psize_0 | 0; + L726 : do { + helper$3 = 1; + HEAP32[sp + 8 >> 2] = $mem; + HEAP32[sp + 24 >> 2] = $10; + HEAP32[sp + 32 >> 2] = $16; + HEAP32[sp + 240 >> 2] = $194; + HEAP32[sp + 664 >> 2] = helper$3; + HEAP32[sp + 688 >> 2] = 0; + HEAP32[sp + 692 >> 2] = 0; + _free$0(sp); + helper$3 = HEAP32[sp + 664 >> 2] | 0; + tempValue = HEAP32[sp + 688 >> 2] | 0; + tempInt = HEAP32[sp + 692 >> 2] | 0; + tempDouble = +HEAPF32[sp + 692 >> 2]; + HEAP32[sp + 688 >> 2] = 0; + HEAP32[sp + 692 >> 2] = 0; + if ((tempValue | 0) == 1) { + break; + } + if ((tempValue | 0) == 2) { + switch (tempInt | 0) { + case 2: + { + break L726; + } } } + } while (0); + HEAP32[$p_0 + 4 >> 2] = $227 | 1; + HEAP32[$189 + $227 >> 2] = $227; + if (($p_0 | 0) != (HEAP32[25] | 0)) { + $psize_1 = $227; + break; } - } while (0); - HEAP32[$p_0 + 4 >> 2] = $227 | 1; - HEAP32[$189 + $227 >> 2] = $227; - if (($p_0 | 0) != (HEAP32[25] | 0)) { - $psize_1 = $227; - break; + HEAP32[22] = $227; + HEAP32[sp + 704 >> 2] = 5; + break OL; + } else { + helper$2 = 1; + } + } + if (helper$2) { + helper$2 = 0; + if (1) { + HEAP32[($mem + (($10 & -8) - 4) | 0) >> 2] = $194 & -2; + HEAP32[$p_0 + 4 >> 2] = $psize_0 | 1; + HEAP32[$189 + $psize_0 >> 2] = $psize_0; + $psize_1 = $psize_0; + } else { + helper$2 = 1; } - HEAP32[22] = $227; - HEAP32[sp + 656 >> 2] = 5; - break OL; - } else { - HEAP32[($mem + (($10 & -8) - 4) | 0) >> 2] = $194 & -2; - HEAP32[$p_0 + 4 >> 2] = $psize_0 | 1; - HEAP32[$189 + $psize_0 >> 2] = $psize_0; - $psize_1 = $psize_0; } } while (0); $390 = $psize_1 >>> 3; @@ -610,7 +674,7 @@ function _free$2(sp) { HEAP32[$F16_0 + 12 >> 2] = $p_0; HEAP32[$p_0 + 8 >> 2] = $F16_0; HEAP32[$p_0 + 12 >> 2] = 120 + ($390 << 1 << 2) | 0; - HEAP32[sp + 656 >> 2] = 5; + HEAP32[sp + 704 >> 2] = 5; break OL; } $414 = $p_0; @@ -635,60 +699,72 @@ function _free$2(sp) { $447 = HEAP32[21] | 0; $448 = 1 << $I18_0; do { - if (($447 & $448 | 0) == 0) { - HEAP32[21] = $447 | $448; - HEAP32[$443 >> 2] = $414; - HEAP32[$p_0 + 24 >> 2] = $443; - HEAP32[$p_0 + 12 >> 2] = $p_0; - HEAP32[$p_0 + 8 >> 2] = $p_0; - } else { - if (($I18_0 | 0) == 31) { - $463 = 0; + helper$5 = 1; + if (helper$5) { + helper$5 = 0; + if (($447 & $448 | 0) == 0) { + HEAP32[21] = $447 | $448; + HEAP32[$443 >> 2] = $414; + HEAP32[$p_0 + 24 >> 2] = $443; + HEAP32[$p_0 + 12 >> 2] = $p_0; + HEAP32[$p_0 + 8 >> 2] = $p_0; } else { - $463 = 25 - ($I18_0 >>> 1) | 0; + helper$5 = 1; } - $K19_0 = $psize_1 << $463; - $T_0 = HEAP32[$443 >> 2] | 0; - while (1) { - if ((HEAP32[$T_0 + 4 >> 2] & -8 | 0) == ($psize_1 | 0)) { - break; - } - $472 = $T_0 + 16 + ($K19_0 >>> 31 << 2) | 0; - $473 = HEAP32[$472 >> 2] | 0; - if (($473 | 0) == 0) { - label = 569; - break; + } + if (helper$5) { + helper$5 = 0; + if (1) { + if (($I18_0 | 0) == 31) { + $463 = 0; } else { - $K19_0 = $K19_0 << 1; - $T_0 = $473; + $463 = 25 - ($I18_0 >>> 1) | 0; } - } - if ((label | 0) == 569) { - if ($472 >>> 0 < (HEAP32[24] | 0) >>> 0) { + $K19_0 = $psize_1 << $463; + $T_0 = HEAP32[$443 >> 2] | 0; + while (1) { + if ((HEAP32[$T_0 + 4 >> 2] & -8 | 0) == ($psize_1 | 0)) { + break; + } + $472 = $T_0 + 16 + ($K19_0 >>> 31 << 2) | 0; + $473 = HEAP32[$472 >> 2] | 0; + if (($473 | 0) == 0) { + label = 569; + break; + } else { + $K19_0 = $K19_0 << 1; + $T_0 = $473; + } + } + if ((label | 0) == 569) { + if ($472 >>> 0 < (HEAP32[24] | 0) >>> 0) { + _abort(); + } else { + HEAP32[$472 >> 2] = $414; + HEAP32[$p_0 + 24 >> 2] = $T_0; + HEAP32[$p_0 + 12 >> 2] = $p_0; + HEAP32[$p_0 + 8 >> 2] = $p_0; + break; + } + } + $486 = $T_0 + 8 | 0; + $487 = HEAP32[$486 >> 2] | 0; + $489 = HEAP32[24] | 0; + if ($T_0 >>> 0 < $489 >>> 0) { + _abort(); + } + if ($487 >>> 0 < $489 >>> 0) { _abort(); } else { - HEAP32[$472 >> 2] = $414; - HEAP32[$p_0 + 24 >> 2] = $T_0; - HEAP32[$p_0 + 12 >> 2] = $p_0; - HEAP32[$p_0 + 8 >> 2] = $p_0; + HEAP32[$487 + 12 >> 2] = $414; + HEAP32[$486 >> 2] = $414; + HEAP32[$p_0 + 8 >> 2] = $487; + HEAP32[$p_0 + 12 >> 2] = $T_0; + HEAP32[$p_0 + 24 >> 2] = 0; break; } - } - $486 = $T_0 + 8 | 0; - $487 = HEAP32[$486 >> 2] | 0; - $489 = HEAP32[24] | 0; - if ($T_0 >>> 0 < $489 >>> 0) { - _abort(); - } - if ($487 >>> 0 < $489 >>> 0) { - _abort(); } else { - HEAP32[$487 + 12 >> 2] = $414; - HEAP32[$486 >> 2] = $414; - HEAP32[$p_0 + 8 >> 2] = $487; - HEAP32[$p_0 + 12 >> 2] = $T_0; - HEAP32[$p_0 + 24 >> 2] = 0; - break; + helper$5 = 1; } } } while (0); @@ -697,7 +773,7 @@ function _free$2(sp) { if (($501 | 0) == 0) { $sp_0_in_i = 536; } else { - HEAP32[sp + 656 >> 2] = 5; + HEAP32[sp + 704 >> 2] = 5; break OL; } while (1) { @@ -710,7 +786,7 @@ function _free$2(sp) { } HEAP32[28] = -1; STACKTOP = sp; - HEAP32[sp + 656 >> 2] = 5; + HEAP32[sp + 704 >> 2] = 5; break OL; } while (0); } From 3b871e28f328c45835f75d29e790bfc808f2f002 Mon Sep 17 00:00:00 2001 From: Anthony Pesch Date: Tue, 13 Aug 2013 17:34:24 -0700 Subject: [PATCH 057/112] make FS.ErrnoError inherit from Error --- src/library_fs.js | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/library_fs.js b/src/library_fs.js index 9c83fcad48f05..f18e48093b8a2 100644 --- a/src/library_fs.js +++ b/src/library_fs.js @@ -27,16 +27,21 @@ mergeInto(LibraryManager.library, { // to modify the filesystem freely before run() is called. ignorePermissions: true, - ErrnoError: function(errno) { - this.errno = errno; - for (var key in ERRNO_CODES) { - if (ERRNO_CODES[key] === errno) { - this.code = key; - break; + ErrnoError: (function() { + function ErrnoError(errno) { + this.errno = errno; + for (var key in ERRNO_CODES) { + if (ERRNO_CODES[key] === errno) { + this.code = key; + break; + } } - } - this.message = ERRNO_MESSAGES[errno] + ' : ' + new Error().stack; - }, + this.message = ERRNO_MESSAGES[errno]; + }; + ErrnoError.prototype = new Error(); + ErrnoError.prototype.constructor = ErrnoError; + return ErrnoError; + }()), handleFSError: function(e) { if (!(e instanceof FS.ErrnoError)) throw e + ' : ' + new Error().stack; From 68dfca4906a5c33a66f3c8cdf7850de970c985bd Mon Sep 17 00:00:00 2001 From: Anthony Pesch Date: Tue, 13 Aug 2013 20:18:33 -0700 Subject: [PATCH 058/112] - made FS.readdir easier to use - tightened up exported node and stream ops for each type --- src/library.js | 2 +- src/library_fs.js | 19 ++++++++----- src/library_memfs.js | 66 ++++++++++++++++++++++++++++++-------------- 3 files changed, 59 insertions(+), 28 deletions(-) diff --git a/src/library.js b/src/library.js index 815badc1b560c..33b1f1aaa2e4b 100644 --- a/src/library.js +++ b/src/library.js @@ -100,7 +100,7 @@ LibraryManager.library = { } var entries; try { - entries = FS.readdir(stream); + entries = FS.readdir(stream.path); } catch (e) { return FS.handleFSError(e); } diff --git a/src/library_fs.js b/src/library_fs.js index 9c83fcad48f05..c9aa9900565a4 100644 --- a/src/library_fs.js +++ b/src/library_fs.js @@ -730,6 +730,9 @@ mergeInto(LibraryManager.library, { }); // use a custom read function stream_ops.read = function(stream, buffer, offset, length, position) { + if (!FS.forceLoadFile(node)) { + throw new FS.ErrnoError(ERRNO_CODES.EIO); + } var contents = stream.node.contents; var size = Math.min(contents.length - position, length); if (contents.slice) { // normal array @@ -1035,7 +1038,7 @@ mergeInto(LibraryManager.library, { FS.hashRemoveNode(old_node); // do the underlying fs rename try { - old_node.node_ops.rename(old_node, new_dir, new_name); + old_dir.node_ops.rename(old_node, new_dir, new_name); } catch (e) { throw e; } finally { @@ -1062,6 +1065,14 @@ mergeInto(LibraryManager.library, { parent.node_ops.rmdir(parent, name); FS.destroyNode(node); }, + readdir: function(path) { + var lookup = FS.lookupPath(path, { follow: true }); + var node = lookup.node; + if (!node.node_ops.readdir) { + throw new FS.ErrnoError(ERRNO_CODES.ENOTDIR); + } + return node.node_ops.readdir(node); + }, unlink: function(path) { var lookup = FS.lookupPath(path, { parent: true }); var parent = lookup.node; @@ -1280,12 +1291,6 @@ mergeInto(LibraryManager.library, { } return stream.stream_ops.llseek(stream, offset, whence); }, - readdir: function(stream) { - if (!stream.stream_ops.readdir) { - throw new FS.ErrnoError(ERRNO_CODES.ENOTDIR); - } - return stream.stream_ops.readdir(stream); - }, read: function(stream, buffer, offset, length, position) { if (length < 0 || position < 0) { throw new FS.ErrnoError(ERRNO_CODES.EINVAL); diff --git a/src/library_memfs.js b/src/library_memfs.js index a044e0c6f723e..0fa6cdfbbd620 100644 --- a/src/library_memfs.js +++ b/src/library_memfs.js @@ -10,16 +10,48 @@ mergeInto(LibraryManager.library, { throw new FS.ErrnoError(ERRNO_CODES.EPERM); } var node = FS.createNode(parent, name, mode, dev); - node.node_ops = MEMFS.node_ops; if (FS.isDir(node.mode)) { - node.stream_ops = MEMFS.stream_ops; + node.node_ops = { + getattr: MEMFS.node_ops.getattr, + setattr: MEMFS.node_ops.setattr, + lookup: MEMFS.node_ops.lookup, + mknod: MEMFS.node_ops.mknod, + mknod: MEMFS.node_ops.mknod, + rename: MEMFS.node_ops.rename, + unlink: MEMFS.node_ops.unlink, + rmdir: MEMFS.node_ops.rmdir, + readdir: MEMFS.node_ops.readdir, + symlink: MEMFS.node_ops.symlink + }; + node.stream_ops = { + llseek: MEMFS.stream_ops.llseek + }; node.contents = {}; } else if (FS.isFile(node.mode)) { - node.stream_ops = MEMFS.stream_ops; + node.node_ops = { + getattr: MEMFS.node_ops.getattr, + setattr: MEMFS.node_ops.setattr + }; + node.stream_ops = { + llseek: MEMFS.stream_ops.llseek, + read: MEMFS.stream_ops.read, + write: MEMFS.stream_ops.write, + allocate: MEMFS.stream_ops.allocate, + mmap: MEMFS.stream_ops.mmap + }; node.contents = []; } else if (FS.isLink(node.mode)) { - node.stream_ops = MEMFS.stream_ops; + node.node_ops = { + getattr: MEMFS.node_ops.getattr, + setattr: MEMFS.node_ops.setattr, + readlink: MEMFS.node_ops.readlink + }; + node.stream_ops = {}; } else if (FS.isChrdev(node.mode)) { + node.node_ops = { + getattr: MEMFS.node_ops.getattr, + setattr: MEMFS.node_ops.setattr + }; node.stream_ops = FS.chrdev_stream_ops; } node.timestamp = Date.now(); @@ -106,6 +138,16 @@ mergeInto(LibraryManager.library, { } delete parent.contents[name]; }, + readdir: function(node) { + var entries = ['.', '..'] + for (var key in node.contents) { + if (!node.contents.hasOwnProperty(key)) { + continue; + } + entries.push(key); + } + return entries; + }, symlink: function(parent, newname, oldpath) { var node = MEMFS.create_node(parent, newname, 0777 | {{{ cDefine('S_IFLNK') }}}, 0); node.link = oldpath; @@ -119,19 +161,6 @@ mergeInto(LibraryManager.library, { }, }, stream_ops: { - open: function(stream) { - if (FS.isDir(stream.node.mode)) { - // cache off the directory entries when open'd - var entries = ['.', '..'] - for (var key in stream.node.contents) { - if (!stream.node.contents.hasOwnProperty(key)) { - continue; - } - entries.push(key); - } - stream.entries = entries; - } - }, read: function(stream, buffer, offset, length, position) { var contents = stream.node.contents; var size = Math.min(contents.length - position, length); @@ -172,9 +201,6 @@ mergeInto(LibraryManager.library, { stream.position = position; return position; }, - readdir: function(stream) { - return stream.entries; - }, allocate: function(stream, offset, length) { var contents = stream.node.contents; var limit = offset + length; From 3426007a589160ac350d3809f2495aef00a61fed Mon Sep 17 00:00:00 2001 From: Alan Kligman Date: Tue, 13 Aug 2013 22:25:52 -0400 Subject: [PATCH 059/112] Add return value to sendmsg. --- src/library.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/library.js b/src/library.js index 815badc1b560c..d4218bb21e043 100644 --- a/src/library.js +++ b/src/library.js @@ -7454,6 +7454,7 @@ LibraryManager.library = { buffer.set(data, info.header.byteLength); connection.send('unreliable', buffer.buffer); + return ret; }, recvmsg__deps: ['$FS', '$Sockets', 'bind', '__setErrNo', '$ERRNO_CODES', 'htons'], From ddc6000c88caafd7ae53f37728e5b315ddd8f877 Mon Sep 17 00:00:00 2001 From: Alan Kligman Date: Tue, 13 Aug 2013 23:21:06 -0400 Subject: [PATCH 060/112] Test for webrtc sockets. --- tests/runner.py | 2 +- tests/sockets/webrtc_host.c | 89 +++++++++++++++++++++++++++++++++++++ tests/sockets/webrtc_peer.c | 81 +++++++++++++++++++++++++++++++++ tests/test_sockets.py | 66 +++++++++++++++++++++++++++ 4 files changed, 237 insertions(+), 1 deletion(-) create mode 100644 tests/sockets/webrtc_host.c create mode 100644 tests/sockets/webrtc_peer.c diff --git a/tests/runner.py b/tests/runner.py index 2f508dfc6404f..af671c11566f7 100755 --- a/tests/runner.py +++ b/tests/runner.py @@ -771,4 +771,4 @@ def btest(self, filename, expected=None, reference=None, force_c=False, referenc else: testRunner = unittest.TextTestRunner(verbosity=2) for suite in suites: - testRunner.run(suite) + testRunner.run(suite) \ No newline at end of file diff --git a/tests/sockets/webrtc_host.c b/tests/sockets/webrtc_host.c new file mode 100644 index 0000000000000..410184adbe01a --- /dev/null +++ b/tests/sockets/webrtc_host.c @@ -0,0 +1,89 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if EMSCRIPTEN +#include +#endif + +#define EXPECTED_BYTES 5 +#define BUFLEN 16 + +int result = 0; +int sock; +char buf[BUFLEN]; +char expected[] = "emscripten"; +struct sockaddr_in si_host, + si_peer; +struct iovec iov[1]; +struct msghdr hdr; +int done = 0; + +void iter(void* arg) { + int n; + n = recvmsg(sock, &hdr, 0); + + if(0 < n) { + done = 1; + fprintf(stderr, "received %d bytes: %s", n, (char*)hdr.msg_iov[0].iov_base); + + shutdown(sock, SHUT_RDWR); + close(sock); + +#if EMSCRIPTEN + int result = 1; + // REPORT_RESULT(); + exit(EXIT_SUCCESS); + emscripten_cancel_main_loop(); +#endif + } else if(EWOULDBLOCK != errno) { + perror("recvmsg failed"); + exit(EXIT_FAILURE); + emscripten_cancel_main_loop(); + } +} + +int main(void) +{ + memset(&si_host, 0, sizeof(struct sockaddr_in)); + memset(&si_peer, 0, sizeof(struct sockaddr_in)); + + si_host.sin_family = AF_INET; + si_host.sin_port = htons(8991); + si_host.sin_addr.s_addr = htonl(INADDR_ANY); + + if(-1 == (sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP))) { + perror("cannot create host socket"); + exit(EXIT_FAILURE); + } + + if(-1 == bind(sock, (struct sockaddr*)&si_host, sizeof(struct sockaddr))) { + perror("cannot bind host socket"); + exit(EXIT_FAILURE); + } + + iov[0].iov_base = buf; + iov[0].iov_len = sizeof(buf); + + memset (&hdr, 0, sizeof (struct msghdr)); + + hdr.msg_name = &si_peer; + hdr.msg_namelen = sizeof(struct sockaddr_in); + hdr.msg_iov = iov; + hdr.msg_iovlen = 1; + +#if EMSCRIPTEN + emscripten_set_main_loop(iter, 0, 0); +#else + while (!done) iter(NULL); +#endif + + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/tests/sockets/webrtc_peer.c b/tests/sockets/webrtc_peer.c new file mode 100644 index 0000000000000..d24979e78f773 --- /dev/null +++ b/tests/sockets/webrtc_peer.c @@ -0,0 +1,81 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if EMSCRIPTEN +#include +#endif + +#define EXPECTED_BYTES 5 +#define BUFLEN 16 +#define HOST_ADDR "10.0.0.1" + +int result = 0; +int sock; +char buf[16] = "emscripten"; +struct sockaddr_in si_host; +struct iovec iov[1]; +struct msghdr hdr; +int done = 0; + +void iter(void* arg) { + int n; + n = sendmsg(sock, &hdr, 0); + + if(0 < n) { + done = 1; + fprintf(stderr, "sent %d bytes: %s", n, (char*)hdr.msg_iov[0].iov_base); + + shutdown(sock, SHUT_RDWR); + close(sock); + + exit(EXIT_SUCCESS); + emscripten_cancel_main_loop(); + } else if(EWOULDBLOCK != errno) { + perror("sendmsg failed"); + exit(EXIT_FAILURE); + emscripten_cancel_main_loop(); + } +} + +int main(void) +{ + memset(&si_host, 0, sizeof(struct sockaddr_in)); + + si_host.sin_family = AF_INET; + si_host.sin_port = htons(8991); + if(0 == inet_pton(AF_INET, HOST_ADDR, &si_host.sin_addr)) { + perror("inet_aton failed"); + exit(EXIT_FAILURE); + } + + if(-1 == (sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP))) { + perror("cannot create socket"); + exit(EXIT_FAILURE); + } + + iov[0].iov_base = buf; + iov[0].iov_len = sizeof(buf); + + memset (&hdr, 0, sizeof (struct msghdr)); + + hdr.msg_name = &si_host; + hdr.msg_namelen = sizeof(struct sockaddr_in); + hdr.msg_iov = iov; + hdr.msg_iovlen = 1; + +#if EMSCRIPTEN + emscripten_set_main_loop(iter, 0, 0); +#else + while (!done) iter(NULL); +#endif + + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/tests/test_sockets.py b/tests/test_sockets.py index 0359367454332..70679b15a9b3b 100644 --- a/tests/test_sockets.py +++ b/tests/test_sockets.py @@ -198,3 +198,69 @@ def test_enet(self): # ]: # with harness: # self.btest(os.path.join('sockets', 'test_enet_client.c'), expected='0', args=['-DSOCKK=9011'] + enet) + + def test_webrtc(self): + host_src = 'webrtc_host.c' + peer_src = 'webrtc_peer.c' + + host_outfile = 'host.html' + peer_outfile = 'peer.html' + + host_filepath = path_from_root('tests', 'sockets', host_src) + temp_host_filepath = os.path.join(self.get_dir(), os.path.basename(host_src)) + with open(host_filepath) as f: host_src = f.read() + with open(temp_host_filepath, 'w') as f: f.write(self.with_report_result(host_src)) + + peer_filepath = path_from_root('tests', 'sockets', peer_src) + temp_peer_filepath = os.path.join(self.get_dir(), os.path.basename(peer_src)) + with open(peer_filepath) as f: peer_src = f.read() + with open(temp_peer_filepath, 'w') as f: f.write(self.with_report_result(peer_src)) + + open(os.path.join(self.get_dir(), 'host_pre.js'), 'w').write(''' + var Module = { + webrtc: { + broker: 'https://mdsw.ch:8080', + session: undefined, + onpeer: function(peer, route) { + window.open('http://localhost:8888/peer.html?' + route); + //iframe = document.createElement("IFRAME"); + //iframe.setAttribute("src", "http://localhost:8888/peer.html?" + route); + //iframe.style.display = "none"; + //document.body.appendChild(iframe); + peer.listen(); + }, + onconnect: function(peer) { + }, + ondisconnect: function(peer) { + }, + onerror: function(error) { + console.error(error); + } + } + }; + ''') + + open(os.path.join(self.get_dir(), 'peer_pre.js'), 'w').write(''' + var Module = { + webrtc: { + broker: 'https://mdsw.ch:8080', + session: window.location.toString().split('?')[1], + onpeer: function(peer, route) { + peer.connect(Module['webrtc']['session']); + }, + onconnect: function(peer) { + }, + ondisconnect: function(peer) { + }, + onerror: function(error) { + console.error(error); + } + } + }; + ''') + + Popen([PYTHON, EMCC, temp_host_filepath, '-o', host_outfile] + ['-s', 'GL_TESTING=1', '--pre-js', 'host_pre.js', '-s', 'SOCKET_WEBRTC=1', '-s', 'SOCKET_DEBUG=1']).communicate() + Popen([PYTHON, EMCC, temp_peer_filepath, '-o', peer_outfile] + ['-s', 'GL_TESTING=1', '--pre-js', 'peer_pre.js', '-s', 'SOCKET_WEBRTC=1', '-s', 'SOCKET_DEBUG=1']).communicate() + + expected = '1' + self.run_browser(host_outfile, '.', ['/report_result?' + e for e in expected]) \ No newline at end of file From 83b0ec50152d58b185e91d10713231dbb790ceae Mon Sep 17 00:00:00 2001 From: Anthony Pesch Date: Tue, 13 Aug 2013 21:24:39 -0700 Subject: [PATCH 061/112] default to mode 0666 --- src/library.js | 2 -- src/library_fs.js | 1 + 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/library.js b/src/library.js index 815badc1b560c..ae5e052534216 100644 --- a/src/library.js +++ b/src/library.js @@ -478,8 +478,6 @@ LibraryManager.library = { open: function(path, oflag, varargs) { // int open(const char *path, int oflag, ...); // http://pubs.opengroup.org/onlinepubs/009695399/functions/open.html - // NOTE: This implementation tries to mimic glibc rather than strictly - // following the POSIX standard. var mode = {{{ makeGetValue('varargs', 0, 'i32') }}}; path = Pointer_stringify(path); try { diff --git a/src/library_fs.js b/src/library_fs.js index 9c83fcad48f05..63f7aed5dd394 100644 --- a/src/library_fs.js +++ b/src/library_fs.js @@ -1202,6 +1202,7 @@ mergeInto(LibraryManager.library, { open: function(path, flags, mode, fd_start, fd_end) { path = PATH.normalize(path); flags = typeof flags === 'string' ? FS.modeStringToFlags(flags) : flags; + mode = typeof mode === 'undefined' ? 0666 : mode; if ((flags & {{{ cDefine('O_CREAT') }}})) { mode = (mode & {{{ cDefine('S_IALLUGO') }}}) | {{{ cDefine('S_IFREG') }}}; } else { From 2d51d99e4f77aed13d05e40b24976b62f8e926d0 Mon Sep 17 00:00:00 2001 From: Alan Kligman Date: Wed, 14 Aug 2013 00:58:28 -0400 Subject: [PATCH 062/112] Minor fixes for webrtc test. --- src/library.js | 7 +++---- tests/runner.py | 2 +- tests/sockets/webrtc_host.c | 2 +- tests/test_sockets.py | 12 +++++++----- 4 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/library.js b/src/library.js index d4218bb21e043..872c33a757074 100644 --- a/src/library.js +++ b/src/library.js @@ -7237,7 +7237,7 @@ LibraryManager.library = { socket__deps: ['$FS', '$Sockets'], socket: function(family, type, protocol) { var INCOMING_QUEUE_LENGTH = 64; - var stream = FS.createStream({ + var info = FS.createStream({ addr: null, port: null, inQueue: new CircularBuffer(INCOMING_QUEUE_LENGTH), @@ -7246,7 +7246,7 @@ LibraryManager.library = { socket: true, stream_ops: {} }); - assert(stream.fd < 64); // select() assumes socket fd values are in 0..63 + assert(info.fd < 64); // select() assumes socket fd values are in 0..63 var stream = type == {{{ cDefine('SOCK_STREAM') }}}; if (protocol) { assert(stream == (protocol == {{{ cDefine('IPPROTO_TCP') }}})); // if stream, must be tcp @@ -7359,8 +7359,7 @@ LibraryManager.library = { } }; }; - - return stream.fd; + return info.fd; }, mkport__deps: ['$Sockets'], diff --git a/tests/runner.py b/tests/runner.py index af671c11566f7..2c6af351cb8dd 100755 --- a/tests/runner.py +++ b/tests/runner.py @@ -480,7 +480,7 @@ def do_GET(s): if 'report_' in s.path: q.put(s.path) else: - filename = s.path[1:] + filename = s.path.split('?')[0][1:] if os.path.exists(filename): s.send_response(200) s.send_header("Content-type", "text/html") diff --git a/tests/sockets/webrtc_host.c b/tests/sockets/webrtc_host.c index 410184adbe01a..770e59e01aa24 100644 --- a/tests/sockets/webrtc_host.c +++ b/tests/sockets/webrtc_host.c @@ -39,7 +39,7 @@ void iter(void* arg) { #if EMSCRIPTEN int result = 1; - // REPORT_RESULT(); + REPORT_RESULT(); exit(EXIT_SUCCESS); emscripten_cancel_main_loop(); #endif diff --git a/tests/test_sockets.py b/tests/test_sockets.py index 70679b15a9b3b..4f6ee2a9c4284 100644 --- a/tests/test_sockets.py +++ b/tests/test_sockets.py @@ -223,10 +223,10 @@ def test_webrtc(self): session: undefined, onpeer: function(peer, route) { window.open('http://localhost:8888/peer.html?' + route); - //iframe = document.createElement("IFRAME"); - //iframe.setAttribute("src", "http://localhost:8888/peer.html?" + route); - //iframe.style.display = "none"; - //document.body.appendChild(iframe); + // iframe = document.createElement("IFRAME"); + // iframe.setAttribute("src", "http://localhost:8888/peer.html?" + route); + // iframe.style.display = "none"; + // document.body.appendChild(iframe); peer.listen(); }, onconnect: function(peer) { @@ -236,7 +236,7 @@ def test_webrtc(self): onerror: function(error) { console.error(error); } - } + }, }; ''') @@ -251,6 +251,8 @@ def test_webrtc(self): onconnect: function(peer) { }, ondisconnect: function(peer) { + // Calling window.close() from this handler hangs my browser, so run it in the next turn + setTimeout(window.close, 0); }, onerror: function(error) { console.error(error); From ca86f56dee399aaf505767a6036bc99fe3764a6e Mon Sep 17 00:00:00 2001 From: Anthony Pesch Date: Wed, 14 Aug 2013 00:27:43 -0700 Subject: [PATCH 063/112] added SDL_AudioDriverName --- src/library_sdl.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/library_sdl.js b/src/library_sdl.js index 92cfc7e508a34..332bf5aeb9744 100644 --- a/src/library_sdl.js +++ b/src/library_sdl.js @@ -755,6 +755,11 @@ var LibrarySDL = { return depth; // all modes are ok. }, + SDL_AudioDriverName__deps: ['SDL_VideoDriverName'], + SDL_AudioDriverName: function(buf, max_size) { + return _SDL_VideoDriverName(buf, max_size); + }, + SDL_VideoDriverName: function(buf, max_size) { if (SDL.startTime === null) { return 0; //return NULL From 564842afc85da50ff768736b3a34d7a8a5d973e8 Mon Sep 17 00:00:00 2001 From: Anthony Pesch Date: Wed, 14 Aug 2013 16:53:55 -0700 Subject: [PATCH 064/112] fixed SDL_Surface struct size --- src/library_sdl.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/library_sdl.js b/src/library_sdl.js index 92cfc7e508a34..71b908c85d31e 100644 --- a/src/library_sdl.js +++ b/src/library_sdl.js @@ -258,7 +258,7 @@ var LibrarySDL = { makeSurface: function(width, height, flags, usePageCanvas, source, rmask, gmask, bmask, amask) { flags = flags || 0; - var surf = _malloc(14*Runtime.QUANTUM_SIZE); // SDL_Surface has 14 fields of quantum size + var surf = _malloc(15*Runtime.QUANTUM_SIZE); // SDL_Surface has 15 fields of quantum size var buffer = _malloc(width*height*4); // TODO: only allocate when locked the first time var pixelFormat = _malloc(18*Runtime.QUANTUM_SIZE); flags |= 1; // SDL_HWSURFACE - this tells SDL_MUSTLOCK that this needs to be locked From 672f2ab151e09de7d62e51ec5a6d029e8b5d02da Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 14 Aug 2013 17:16:26 -0700 Subject: [PATCH 065/112] do not fail if no read() in the shell, which is the case with jsc --- src/shell.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/shell.js b/src/shell.js index bac4eaa32e731..f91aa96a07260 100644 --- a/src/shell.js +++ b/src/shell.js @@ -74,7 +74,12 @@ else if (ENVIRONMENT_IS_SHELL) { Module['print'] = print; if (typeof printErr != 'undefined') Module['printErr'] = printErr; // not present in v8 or older sm - Module['read'] = read; + if (typeof read != 'undefined') { + Module['read'] = read; + } else { + Module['read'] = function() { throw 'no read() available (jsc?)' }; + } + Module['readBinary'] = function(f) { return read(f, 'binary'); }; From 139c7755ed9b35b4c37c394c48bad58cf067e52b Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 14 Aug 2013 17:26:09 -0700 Subject: [PATCH 066/112] update emcc comment --- emcc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/emcc b/emcc index b63eada00079c..6e0761a76d6e0 100755 --- a/emcc +++ b/emcc @@ -137,7 +137,7 @@ Options that are modified or new in %s include: opt levels, see apply_opt_level() in tools/shared.py and also src/settings.js.) -O2 As -O1, plus the relooper (loop recreation), - LLVM -O2 optimizations, and + LLVM -O3 optimizations, and -s ALIASING_FUNCTION_POINTERS=1 From 038eee5c2c1dae00e0b76345c4530b2d66150dc8 Mon Sep 17 00:00:00 2001 From: Ryan Kelly Date: Thu, 15 Aug 2013 12:54:18 +1000 Subject: [PATCH 067/112] Ensure hashName always returns a positive integer. --- src/library_fs.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/library_fs.js b/src/library_fs.js index d6cfe7f55419f..8ea6b06f518d3 100644 --- a/src/library_fs.js +++ b/src/library_fs.js @@ -51,7 +51,7 @@ mergeInto(LibraryManager.library, { for (var i = 0; i < name.length; i++) { hash = ((hash << 5) - hash + name.charCodeAt(i)) | 0; } - return (parentid + hash) % FS.name_table.length; + return ((parentid + hash) >>> 0) % FS.name_table.length; }, hashAddNode: function(node) { var hash = FS.hashName(node.parent.id, node.name); From a0bcc754bea4485c24427001306284043925e9b1 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 14 Aug 2013 20:49:26 -0700 Subject: [PATCH 068/112] disable optimizeShifts; fixes #1518 --- emcc | 3 --- tools/js-optimizer.js | 5 ++++- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/emcc b/emcc index 6e0761a76d6e0..df2ef38cd301c 100755 --- a/emcc +++ b/emcc @@ -1621,9 +1621,6 @@ try: js_optimizer_queue += [get_eliminate(), 'simplifyExpressions'] - if shared.Settings.RELOOP and not shared.Settings.ASM_JS: - js_optimizer_queue += ['optimizeShiftsAggressive', get_eliminate()] # aggressive shifts optimization requires loops, it breaks on switches - if closure and not shared.Settings.ASM_JS: flush_js_optimizer_queue() diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js index 7d72b7b863f76..e61317afb5eb5 100644 --- a/tools/js-optimizer.js +++ b/tools/js-optimizer.js @@ -801,7 +801,10 @@ function simplifyExpressions(ast) { // HEAP[x >> 2] // very often. We can in some cases do the shift on the variable itself when it is set, // to greatly reduce the number of shift operations. -// TODO: when shifting a variable, if there are other uses, keep an unshifted version too, to prevent slowdowns? +// XXX this optimization is deprecated and currently invalid: does not handle overflows +// or non-aligned (round numbers, x >> 2 is a multiple of 4). Both are ok to assume +// for pointers (undefined behavior otherwise), but invalid in general, and we do +// no sufficiently-well distinguish the cases. function optimizeShiftsInternal(ast, conservative) { var MAX_SHIFTS = 3; traverseGeneratedFunctions(ast, function(fun) { From 9d73d7ce8abb9b2049de489f76e9b79fbf1770f5 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 15 Aug 2013 10:37:43 -0700 Subject: [PATCH 069/112] spam abort() messages to stderr as well --- src/postamble.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/postamble.js b/src/postamble.js index c4ca3aae686ca..08c3a9d8b4ebe 100644 --- a/src/postamble.js +++ b/src/postamble.js @@ -127,6 +127,7 @@ Module['exit'] = Module.exit = exit; function abort(text) { if (text) { Module.print(text); + Module.printErr(text); } ABORT = true; From ced412e4d5a26ad59a007b9e27d02c2e9ed99d15 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 15 Aug 2013 10:50:51 -0700 Subject: [PATCH 070/112] assert in btest on successful compilation --- tests/runner.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/runner.py b/tests/runner.py index 2c6af351cb8dd..8feb83f9cb150 100755 --- a/tests/runner.py +++ b/tests/runner.py @@ -649,6 +649,7 @@ def btest(self, filename, expected=None, reference=None, force_c=False, referenc self.reftest(path_from_root('tests', reference)) args = args + ['--pre-js', 'reftest.js', '-s', 'GL_TESTING=1'] Popen([PYTHON, EMCC, temp_filepath, '-o', outfile] + args).communicate() + assert os.path.exists(outfile) if type(expected) is str: expected = [expected] self.run_browser(outfile, message, ['/report_result?' + e for e in expected]) @@ -771,4 +772,5 @@ def btest(self, filename, expected=None, reference=None, force_c=False, referenc else: testRunner = unittest.TextTestRunner(verbosity=2) for suite in suites: - testRunner.run(suite) \ No newline at end of file + testRunner.run(suite) + From 19047555b54a8190a927e77f2d1c73410ad4fd0a Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 15 Aug 2013 10:52:03 -0700 Subject: [PATCH 071/112] make legacy gl emulation an opt-in option, LEGACY_GL_EMULATION --- src/library_gl.js | 40 ++++++++++++---- src/settings.js | 5 +- tests/test_browser.py | 106 ++++++++++++++++++++---------------------- 3 files changed, 85 insertions(+), 66 deletions(-) diff --git a/src/library_gl.js b/src/library_gl.js index 8c724245cd443..17595021c05e1 100644 --- a/src/library_gl.js +++ b/src/library_gl.js @@ -1285,7 +1285,7 @@ var LibraryGL = { return Module.ctx.isFramebuffer(fb); }, -#if DISABLE_GL_EMULATION == 0 +#if LEGACY_GL_EMULATION // GL emulation: provides misc. functionality not present in OpenGL ES 2.0 or WebGL @@ -1323,7 +1323,7 @@ var LibraryGL = { GLEmulation.fogColor = new Float32Array(4); // Add some emulation workarounds - Module.printErr('WARNING: using emscripten GL emulation. This is a collection of limited workarounds, do not expect it to work. (If you do not want this, build with -s DISABLE_GL_EMULATION=1)'); + Module.printErr('WARNING: using emscripten GL emulation. This is a collection of limited workarounds, do not expect it to work.'); #if GL_UNSAFE_OPTS == 0 Module.printErr('WARNING: using emscripten GL emulation unsafe opts. If weirdness happens, try -s GL_UNSAFE_OPTS=0'); #endif @@ -4202,7 +4202,31 @@ var LibraryGL = { glBindVertexArrayOES: 'glBindVertexArray', glFramebufferTexture2DOES: 'glFramebufferTexture2D', -#endif // DISABLE_GL_EMULATION == 0 +#else // LEGACY_GL_EMULATION + + // Warn if code tries to use various emulation stuff, when emulation is disabled + glVertexPointer__deps: [function() { + error('Legacy GL function (glVertexPointer) called. You need to compile with -s LEGACY_GL_EMULATION=1 to enable legacy GL emulation.'); + }], + glVertexPointer: function(){}, + glGenVertexArrays__deps: [function() { + error('Legacy GL function (glGenVertexArrays) called. You need to compile with -s LEGACY_GL_EMULATION=1 to enable legacy GL emulation.'); + }], + glGenVertexArrays: function(){}, + glMatrixMode__deps: [function() { + error('Legacy GL function (glMatrixMode) called. You need to compile with -s LEGACY_GL_EMULATION=1 to enable legacy GL emulation.'); + }], + glMatrixMode: function(){}, + glBegin__deps: [function() { + error('Legacy GL function (glBegin) called. You need to compile with -s LEGACY_GL_EMULATION=1 to enable legacy GL emulation.'); + }], + glBegin: function(){}, + glLoadIdentity__deps: [function() { + error('Legacy GL function (glLoadIdentity) called. You need to compile with -s LEGACY_GL_EMULATION=1 to enable legacy GL emulation.'); + }], + glLoadIdentity: function(){}, + +#endif // LEGACY_GL_EMULATION // GLU @@ -4438,7 +4462,7 @@ var LibraryGL = { autoAddDeps(LibraryGL, '$GL'); -if (!DISABLE_GL_EMULATION) { +if (LEGACY_GL_EMULATION) { // Emulation requires everything else, potentially LibraryGL.$GLEmulation__deps = LibraryGL.$GLEmulation__deps.slice(0); // the __deps object is shared var glFuncs = []; @@ -4455,11 +4479,11 @@ if (!DISABLE_GL_EMULATION) { } }); - if (FORCE_GL_EMULATION) { - LibraryGL.glDrawElements__deps = LibraryGL.glDrawElements__deps.concat('$GLEmulation'); - LibraryGL.glDrawArrays__deps = LibraryGL.glDrawArrays__deps.concat('$GLEmulation'); - } + LibraryGL.glDrawElements__deps = LibraryGL.glDrawElements__deps.concat('$GLEmulation'); + LibraryGL.glDrawArrays__deps = LibraryGL.glDrawArrays__deps.concat('$GLEmulation'); } mergeInto(LibraryManager.library, LibraryGL); +assert(!(FULL_ES2 && LEGACY_GL_EMULATION), 'cannot emulate both ES2 and legacy GL'); + diff --git a/src/settings.js b/src/settings.js index 3ecac040ccdb6..8757f7b9bdf37 100644 --- a/src/settings.js +++ b/src/settings.js @@ -191,9 +191,8 @@ var GL_TESTING = 0; // When enabled, sets preserveDrawingBuffer in the context, var GL_MAX_TEMP_BUFFER_SIZE = 2097152; // How large GL emulation temp buffers are var GL_UNSAFE_OPTS = 1; // Enables some potentially-unsafe optimizations in GL emulation code var FULL_ES2 = 0; // Forces support for all GLES2 features, not just the WebGL-friendly subset. -var FORCE_GL_EMULATION = 0; // Forces inclusion of full GL emulation code. -var DISABLE_GL_EMULATION = 0; // Disable inclusion of full GL emulation code. Useful when you don't want emulation - // but do need INCLUDE_FULL_LIBRARY or MAIN_MODULE. +var LEGACY_GL_EMULATION = 0; // Includes code to emulate various desktop GL features. Incomplete but useful + // in some cases, see https://github.com/kripken/emscripten/wiki/OpenGL-support var STB_IMAGE = 0; // Enables building of stb-image, a tiny public-domain library for decoding images, allowing // decoding of images without using the browser's built-in decoders. The benefit is that this diff --git a/tests/test_browser.py b/tests/test_browser.py index be2c388b5d155..cd5668a301e27 100644 --- a/tests/test_browser.py +++ b/tests/test_browser.py @@ -620,7 +620,7 @@ def test_sdl_stb_image_data(self): def test_sdl_canvas(self): open(os.path.join(self.get_dir(), 'sdl_canvas.c'), 'w').write(self.with_report_result(open(path_from_root('tests', 'sdl_canvas.c')).read())) - Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'sdl_canvas.c'), '-o', 'page.html']).communicate() + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'sdl_canvas.c'), '-o', 'page.html', '-s', 'LEGACY_GL_EMULATION=1']).communicate() self.run_browser('page.html', '', '/report_result?1') def test_sdl_key(self): @@ -843,50 +843,50 @@ def test_sdl_gl_read(self): def test_sdl_ogl(self): shutil.copyfile(path_from_root('tests', 'screenshot.png'), os.path.join(self.get_dir(), 'screenshot.png')) self.btest('sdl_ogl.c', reference='screenshot-gray-purple.png', reference_slack=1, - args=['-O2', '--minify', '0', '--preload-file', 'screenshot.png'], + args=['-O2', '--minify', '0', '--preload-file', 'screenshot.png', '-s', 'LEGACY_GL_EMULATION=1'], message='You should see an image with gray at the top.') def test_sdl_ogl_defaultmatrixmode(self): shutil.copyfile(path_from_root('tests', 'screenshot.png'), os.path.join(self.get_dir(), 'screenshot.png')) self.btest('sdl_ogl_defaultMatrixMode.c', reference='screenshot-gray-purple.png', reference_slack=1, - args=['--minify', '0', '--preload-file', 'screenshot.png'], + args=['--minify', '0', '--preload-file', 'screenshot.png', '-s', 'LEGACY_GL_EMULATION=1'], message='You should see an image with gray at the top.') def test_sdl_ogl_p(self): # Immediate mode with pointers shutil.copyfile(path_from_root('tests', 'screenshot.png'), os.path.join(self.get_dir(), 'screenshot.png')) self.btest('sdl_ogl_p.c', reference='screenshot-gray.png', reference_slack=1, - args=['--preload-file', 'screenshot.png'], + args=['--preload-file', 'screenshot.png', '-s', 'LEGACY_GL_EMULATION=1'], message='You should see an image with gray at the top.') def test_sdl_fog_simple(self): shutil.copyfile(path_from_root('tests', 'screenshot.png'), os.path.join(self.get_dir(), 'screenshot.png')) self.btest('sdl_fog_simple.c', reference='screenshot-fog-simple.png', - args=['-O2', '--minify', '0', '--preload-file', 'screenshot.png'], + args=['-O2', '--minify', '0', '--preload-file', 'screenshot.png', '-s', 'LEGACY_GL_EMULATION=1'], message='You should see an image with fog.') def test_sdl_fog_negative(self): shutil.copyfile(path_from_root('tests', 'screenshot.png'), os.path.join(self.get_dir(), 'screenshot.png')) self.btest('sdl_fog_negative.c', reference='screenshot-fog-negative.png', - args=['--preload-file', 'screenshot.png'], + args=['--preload-file', 'screenshot.png', '-s', 'LEGACY_GL_EMULATION=1'], message='You should see an image with fog.') def test_sdl_fog_density(self): shutil.copyfile(path_from_root('tests', 'screenshot.png'), os.path.join(self.get_dir(), 'screenshot.png')) self.btest('sdl_fog_density.c', reference='screenshot-fog-density.png', - args=['--preload-file', 'screenshot.png'], + args=['--preload-file', 'screenshot.png', '-s', 'LEGACY_GL_EMULATION=1'], message='You should see an image with fog.') def test_sdl_fog_exp2(self): shutil.copyfile(path_from_root('tests', 'screenshot.png'), os.path.join(self.get_dir(), 'screenshot.png')) self.btest('sdl_fog_exp2.c', reference='screenshot-fog-exp2.png', - args=['--preload-file', 'screenshot.png'], + args=['--preload-file', 'screenshot.png', '-s', 'LEGACY_GL_EMULATION=1'], message='You should see an image with fog.') def test_sdl_fog_linear(self): shutil.copyfile(path_from_root('tests', 'screenshot.png'), os.path.join(self.get_dir(), 'screenshot.png')) self.btest('sdl_fog_linear.c', reference='screenshot-fog-linear.png', reference_slack=1, - args=['--preload-file', 'screenshot.png'], + args=['--preload-file', 'screenshot.png', '-s', 'LEGACY_GL_EMULATION=1'], message='You should see an image with fog.') def test_openal_playback(self): @@ -903,7 +903,7 @@ def test_openal_buffers(self): def test_glfw(self): open(os.path.join(self.get_dir(), 'glfw.c'), 'w').write(self.with_report_result(open(path_from_root('tests', 'glfw.c')).read())) - Popen([PYTHON, EMCC, '-O2', os.path.join(self.get_dir(), 'glfw.c'), '-o', 'page.html']).communicate() + Popen([PYTHON, EMCC, '-O2', os.path.join(self.get_dir(), 'glfw.c'), '-o', 'page.html', '-s', 'LEGACY_GL_EMULATION=1']).communicate() self.run_browser('page.html', '', '/report_result?1') def test_egl_width_height(self): @@ -1063,17 +1063,13 @@ def test_glgears(self): def test_glgears_animation(self): es2_suffix = ['', '_full', '_full_944'] for full_es2 in [0, 1, 2]: - for emulation in [0, 1]: - if full_es2 and emulation: continue - print full_es2, emulation - Popen([PYTHON, EMCC, path_from_root('tests', 'hello_world_gles%s.c' % es2_suffix[full_es2]), '-o', 'something.html', - '-DHAVE_BUILTIN_SINCOS', '-s', 'GL_TESTING=1', - '--shell-file', path_from_root('tests', 'hello_world_gles_shell.html')] + - (['-s', 'FORCE_GL_EMULATION=1'] if emulation else []) + - (['-s', 'FULL_ES2=1'] if full_es2 else []), - ).communicate() - self.run_browser('something.html', 'You should see animating gears.', '/report_gl_result?true') - assert ('var GLEmulation' in open(self.in_dir('something.html')).read()) == emulation, "emulation code should be added when asked for" + print full_es2 + Popen([PYTHON, EMCC, path_from_root('tests', 'hello_world_gles%s.c' % es2_suffix[full_es2]), '-o', 'something.html', + '-DHAVE_BUILTIN_SINCOS', '-s', 'GL_TESTING=1', + '--shell-file', path_from_root('tests', 'hello_world_gles_shell.html')] + + (['-s', 'FULL_ES2=1'] if full_es2 else []), + ).communicate() + self.run_browser('something.html', 'You should see animating gears.', '/report_gl_result?true') def test_fulles2_sdlproc(self): self.btest('full_es2_sdlproc.c', '1', args=['-s', 'GL_TESTING=1', '-DHAVE_BUILTIN_SINCOS', '-s', 'FULL_ES2=1']) @@ -1163,90 +1159,90 @@ def test_glgetattachedshaders(self): self.btest('glgetattachedshaders.c', '1') def test_sdlglshader(self): - self.btest('sdlglshader.c', reference='sdlglshader.png', args=['-O2', '--closure', '1']) + self.btest('sdlglshader.c', reference='sdlglshader.png', args=['-O2', '--closure', '1', '-s', 'LEGACY_GL_EMULATION=1']) def test_gl_ps(self): # pointers and a shader shutil.copyfile(path_from_root('tests', 'screenshot.png'), os.path.join(self.get_dir(), 'screenshot.png')) - self.btest('gl_ps.c', reference='gl_ps.png', args=['--preload-file', 'screenshot.png'], reference_slack=1) + self.btest('gl_ps.c', reference='gl_ps.png', args=['--preload-file', 'screenshot.png', '-s', 'LEGACY_GL_EMULATION=1'], reference_slack=1) def test_gl_ps_packed(self): # packed data that needs to be strided shutil.copyfile(path_from_root('tests', 'screenshot.png'), os.path.join(self.get_dir(), 'screenshot.png')) - self.btest('gl_ps_packed.c', reference='gl_ps.png', args=['--preload-file', 'screenshot.png'], reference_slack=1) + self.btest('gl_ps_packed.c', reference='gl_ps.png', args=['--preload-file', 'screenshot.png', '-s', 'LEGACY_GL_EMULATION=1'], reference_slack=1) def test_gl_ps_strides(self): shutil.copyfile(path_from_root('tests', 'screenshot.png'), os.path.join(self.get_dir(), 'screenshot.png')) - self.btest('gl_ps_strides.c', reference='gl_ps_strides.png', args=['--preload-file', 'screenshot.png']) + self.btest('gl_ps_strides.c', reference='gl_ps_strides.png', args=['--preload-file', 'screenshot.png', '-s', 'LEGACY_GL_EMULATION=1']) def test_gl_renderers(self): - self.btest('gl_renderers.c', reference='gl_renderers.png', args=['-s', 'GL_UNSAFE_OPTS=0']) + self.btest('gl_renderers.c', reference='gl_renderers.png', args=['-s', 'GL_UNSAFE_OPTS=0', '-s', 'LEGACY_GL_EMULATION=1']) def test_gl_stride(self): - self.btest('gl_stride.c', reference='gl_stride.png', args=['-s', 'GL_UNSAFE_OPTS=0']) + self.btest('gl_stride.c', reference='gl_stride.png', args=['-s', 'GL_UNSAFE_OPTS=0', '-s', 'LEGACY_GL_EMULATION=1']) def test_matrix_identity(self): - self.btest('gl_matrix_identity.c', expected=['-1882984448', '460451840']) + self.btest('gl_matrix_identity.c', expected=['-1882984448', '460451840'], args=['-s', 'LEGACY_GL_EMULATION=1']) def test_cubegeom_pre(self): - self.btest('cubegeom_pre.c', reference='cubegeom_pre.png') + self.btest('cubegeom_pre.c', reference='cubegeom_pre.png', args=['-s', 'LEGACY_GL_EMULATION=1']) def test_cubegeom_pre2(self): - self.btest('cubegeom_pre2.c', reference='cubegeom_pre2.png', args=['-s', 'GL_DEBUG=1']) # some coverage for GL_DEBUG not breaking the build + self.btest('cubegeom_pre2.c', reference='cubegeom_pre2.png', args=['-s', 'GL_DEBUG=1', '-s', 'LEGACY_GL_EMULATION=1']) # some coverage for GL_DEBUG not breaking the build def test_cubegeom_pre3(self): - self.btest('cubegeom_pre3.c', reference='cubegeom_pre2.png') + self.btest('cubegeom_pre3.c', reference='cubegeom_pre2.png', args=['-s', 'LEGACY_GL_EMULATION=1']) def test_cubegeom(self): - self.btest('cubegeom.c', args=['-O2', '-g'], reference='cubegeom.png') + self.btest('cubegeom.c', reference='cubegeom.png', args=['-O2', '-g', '-s', 'LEGACY_GL_EMULATION=1']) def test_cubegeom_glew(self): - self.btest('cubegeom_glew.c', args=['-O2', '--closure', '1'], reference='cubegeom.png') + self.btest('cubegeom_glew.c', reference='cubegeom.png', args=['-O2', '--closure', '1', '-s', 'LEGACY_GL_EMULATION=1']) def test_cubegeom_color(self): - self.btest('cubegeom_color.c', reference='cubegeom_color.png') + self.btest('cubegeom_color.c', reference='cubegeom_color.png', args=['-s', 'LEGACY_GL_EMULATION=1']) def test_cubegeom_normal(self): - self.btest('cubegeom_normal.c', reference='cubegeom_normal.png') + self.btest('cubegeom_normal.c', reference='cubegeom_normal.png', args=['-s', 'LEGACY_GL_EMULATION=1']) def test_cubegeom_normal_dap(self): # draw is given a direct pointer to clientside memory, no element array buffer - self.btest('cubegeom_normal_dap.c', reference='cubegeom_normal.png') + self.btest('cubegeom_normal_dap.c', reference='cubegeom_normal.png', args=['-s', 'LEGACY_GL_EMULATION=1']) def test_cubegeom_normal_dap_far(self): # indices do nto start from 0 - self.btest('cubegeom_normal_dap_far.c', reference='cubegeom_normal.png') + self.btest('cubegeom_normal_dap_far.c', reference='cubegeom_normal.png', args=['-s', 'LEGACY_GL_EMULATION=1']) def test_cubegeom_normal_dap_far_range(self): # glDrawRangeElements - self.btest('cubegeom_normal_dap_far_range.c', reference='cubegeom_normal.png') + self.btest('cubegeom_normal_dap_far_range.c', reference='cubegeom_normal.png', args=['-s', 'LEGACY_GL_EMULATION=1']) def test_cubegeom_normal_dap_far_glda(self): # use glDrawArrays - self.btest('cubegeom_normal_dap_far_glda.c', reference='cubegeom_normal_dap_far_glda.png') + self.btest('cubegeom_normal_dap_far_glda.c', reference='cubegeom_normal_dap_far_glda.png', args=['-s', 'LEGACY_GL_EMULATION=1']) def test_cubegeom_normal_dap_far_glda_quad(self): # with quad - self.btest('cubegeom_normal_dap_far_glda_quad.c', reference='cubegeom_normal_dap_far_glda_quad.png') + self.btest('cubegeom_normal_dap_far_glda_quad.c', reference='cubegeom_normal_dap_far_glda_quad.png', args=['-s', 'LEGACY_GL_EMULATION=1']) def test_cubegeom_mt(self): - self.btest('cubegeom_mt.c', reference='cubegeom_mt.png') # multitexture + self.btest('cubegeom_mt.c', reference='cubegeom_mt.png', args=['-s', 'LEGACY_GL_EMULATION=1']) # multitexture def test_cubegeom_color2(self): - self.btest('cubegeom_color2.c', reference='cubegeom_color2.png') + self.btest('cubegeom_color2.c', reference='cubegeom_color2.png', args=['-s', 'LEGACY_GL_EMULATION=1']) def test_cubegeom_texturematrix(self): - self.btest('cubegeom_texturematrix.c', reference='cubegeom_texturematrix.png') + self.btest('cubegeom_texturematrix.c', reference='cubegeom_texturematrix.png', args=['-s', 'LEGACY_GL_EMULATION=1']) def test_cubegeom_fog(self): - self.btest('cubegeom_fog.c', reference='cubegeom_fog.png') + self.btest('cubegeom_fog.c', reference='cubegeom_fog.png', args=['-s', 'LEGACY_GL_EMULATION=1']) def test_cubegeom_pre_vao(self): - self.btest('cubegeom_pre_vao.c', reference='cubegeom_pre_vao.png') + self.btest('cubegeom_pre_vao.c', reference='cubegeom_pre_vao.png', args=['-s', 'LEGACY_GL_EMULATION=1']) def test_cubegeom_pre2_vao(self): - self.btest('cubegeom_pre2_vao.c', reference='cubegeom_pre_vao.png') + self.btest('cubegeom_pre2_vao.c', reference='cubegeom_pre_vao.png', args=['-s', 'LEGACY_GL_EMULATION=1']) def test_cubegeom_pre2_vao2(self): - self.btest('cubegeom_pre2_vao2.c', reference='cubegeom_pre2_vao2.png') + self.btest('cubegeom_pre2_vao2.c', reference='cubegeom_pre2_vao2.png', args=['-s', 'LEGACY_GL_EMULATION=1']) def test_cube_explosion(self): - self.btest('cube_explosion.c', reference='cube_explosion.png') + self.btest('cube_explosion.c', reference='cube_explosion.png', args=['-s', 'LEGACY_GL_EMULATION=1']) def test_sdl_canvas_blank(self): self.btest('sdl_canvas_blank.c', reference='sdl_canvas_blank.png') @@ -1298,11 +1294,11 @@ def test_sdl_surface_refcount(self): def test_glbegin_points(self): shutil.copyfile(path_from_root('tests', 'screenshot.png'), os.path.join(self.get_dir(), 'screenshot.png')) - self.btest('glbegin_points.c', reference='glbegin_points.png', args=['--preload-file', 'screenshot.png']) + self.btest('glbegin_points.c', reference='glbegin_points.png', args=['--preload-file', 'screenshot.png', '-s', 'LEGACY_GL_EMULATION=1']) def test_s3tc(self): shutil.copyfile(path_from_root('tests', 'screenshot.dds'), os.path.join(self.get_dir(), 'screenshot.dds')) - self.btest('s3tc.c', reference='s3tc.png', args=['--preload-file', 'screenshot.dds']) + self.btest('s3tc.c', reference='s3tc.png', args=['--preload-file', 'screenshot.dds', '-s', 'LEGACY_GL_EMULATION=1']) def test_s3tc_crunch(self): shutil.copyfile(path_from_root('tests', 'ship.dds'), 'ship.dds') @@ -1313,7 +1309,7 @@ def test_s3tc_crunch(self): shutil.move('ship.dds', 'ship.donotfindme.dds') # make sure we load from the compressed shutil.move('bloom.dds', 'bloom.donotfindme.dds') # make sure we load from the compressed shutil.move('water.dds', 'water.donotfindme.dds') # make sure we load from the compressed - self.btest('s3tc_crunch.c', reference='s3tc_crunch.png', reference_slack=11, args=['--pre-js', 'pre.js']) + self.btest('s3tc_crunch.c', reference='s3tc_crunch.png', reference_slack=11, args=['--pre-js', 'pre.js', '-s', 'LEGACY_GL_EMULATION=1']) def test_s3tc_crunch_split(self): # load several datafiles/outputs of file packager shutil.copyfile(path_from_root('tests', 'ship.dds'), 'ship.dds') @@ -1324,14 +1320,14 @@ def test_s3tc_crunch_split(self): # load several datafiles/outputs of file packa shutil.move('ship.dds', 'ship.donotfindme.dds') # make sure we load from the compressed shutil.move('bloom.dds', 'bloom.donotfindme.dds') # make sure we load from the compressed shutil.move('water.dds', 'water.donotfindme.dds') # make sure we load from the compressed - self.btest('s3tc_crunch.c', reference='s3tc_crunch.png', reference_slack=11, args=['--pre-js', 'asset_a.js', '--pre-js', 'asset_b.js']) + self.btest('s3tc_crunch.c', reference='s3tc_crunch.png', reference_slack=11, args=['--pre-js', 'asset_a.js', '--pre-js', 'asset_b.js', '-s', 'LEGACY_GL_EMULATION=1']) def test_aniso(self): shutil.copyfile(path_from_root('tests', 'water.dds'), 'water.dds') - self.btest('aniso.c', reference='aniso.png', reference_slack=2, args=['--preload-file', 'water.dds']) + self.btest('aniso.c', reference='aniso.png', reference_slack=2, args=['--preload-file', 'water.dds', '-s', 'LEGACY_GL_EMULATION=1']) def test_tex_nonbyte(self): - self.btest('tex_nonbyte.c', reference='tex_nonbyte.png') + self.btest('tex_nonbyte.c', reference='tex_nonbyte.png', args=['-s', 'LEGACY_GL_EMULATION=1']) def test_float_tex(self): self.btest('float_tex.cpp', reference='float_tex.png') @@ -1340,7 +1336,7 @@ def test_subdata(self): self.btest('gl_subdata.cpp', reference='float_tex.png') def test_perspective(self): - self.btest('perspective.c', reference='perspective.png') + self.btest('perspective.c', reference='perspective.png', args=['-s', 'LEGACY_GL_EMULATION=1']) def test_runtimelink(self): return self.skip('shared libs are deprecated') From deb278c645e1f0c8a5584cc3e0461086860d0e2f Mon Sep 17 00:00:00 2001 From: Anthony Pesch Date: Thu, 15 Aug 2013 12:14:39 -0700 Subject: [PATCH 072/112] move shader compile status check behind GL_DEBUG --- src/library_gl.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/library_gl.js b/src/library_gl.js index 17595021c05e1..89a13bbe6ad6e 100644 --- a/src/library_gl.js +++ b/src/library_gl.js @@ -1618,17 +1618,15 @@ var LibraryGL = { var glCompileShader = _glCompileShader; _glCompileShader = function(shader) { Module.ctx.compileShader(GL.shaders[shader]); +#if GL_DEBUG if (!Module.ctx.getShaderParameter(GL.shaders[shader], Module.ctx.COMPILE_STATUS)) { Module.printErr('Failed to compile shader: ' + Module.ctx.getShaderInfoLog(GL.shaders[shader])); Module.printErr('Info: ' + JSON.stringify(GL.shaderInfos[shader])); -#if GL_DEBUG Module.printErr('Original source: ' + GL.shaderOriginalSources[shader]); Module.printErr('Source: ' + GL.shaderSources[shader]); throw 'Shader compilation halt'; -#else - Module.printErr('Enable GL_DEBUG to see shader source'); -#endif } +#endif }; GL.programShaders = {}; From 9b908451c9fb68341f6c69c98e36ecb891e66968 Mon Sep 17 00:00:00 2001 From: Anthony Pesch Date: Thu, 15 Aug 2013 12:52:42 -0700 Subject: [PATCH 073/112] expose setCanvasSize --- src/library_browser.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/library_browser.js b/src/library_browser.js index f65791e4c0ba2..511e158ee4c22 100644 --- a/src/library_browser.js +++ b/src/library_browser.js @@ -6,6 +6,7 @@ mergeInto(LibraryManager.library, { $Browser__deps: ['$PATH'], $Browser__postset: 'Module["requestFullScreen"] = function(lockPointer, resizeCanvas) { Browser.requestFullScreen(lockPointer, resizeCanvas) };\n' + // exports 'Module["requestAnimationFrame"] = function(func) { Browser.requestAnimationFrame(func) };\n' + + 'Module["setCanvasSize"] = function(width, height, noUpdates) { Browser.setCanvasSize(width, height, noUpdates) };\n' + 'Module["pauseMainLoop"] = function() { Browser.mainLoop.pause() };\n' + 'Module["resumeMainLoop"] = function() { Browser.mainLoop.resume() };\n' + 'Module["getUserMedia"] = function() { Browser.getUserMedia() }', From 4e0c5a89b387d5f4ca8248c8b31e2db136652a64 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 15 Aug 2013 17:45:33 -0700 Subject: [PATCH 074/112] make find_bigfuncs work on ll and js --- tools/find_bigfuncs.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/find_bigfuncs.py b/tools/find_bigfuncs.py index 6fdec3a98f692..79136343c7199 100644 --- a/tools/find_bigfuncs.py +++ b/tools/find_bigfuncs.py @@ -1,5 +1,5 @@ ''' -Simple tool to find big functions in an .ll file. +Simple tool to find big functions in a js or ll file ''' import os, sys, re @@ -11,7 +11,7 @@ data = [] for line in open(filename): i += 1 - if line.startswith('function '): + if line.startswith(('function ', 'define ')): start = i curr = line elif line.startswith('}') and curr: From 10d1cb9f3e515778e7841b61df4c6ad94c3d8352 Mon Sep 17 00:00:00 2001 From: Bruce Mitchener Date: Fri, 16 Aug 2013 12:59:05 +0700 Subject: [PATCH 075/112] Add compat headers, move some things to them. This is groundwork for landing musl includes which remove some of the deprecated functions or very old BSDisms. --- system/include/compat/math.h | 14 ++++++++++++++ system/include/compat/stdlib.h | 16 ++++++++++++++++ system/include/compat/string.h | 17 +++++++++++++++++ system/include/compat/sys/stat.h | 20 ++++++++++++++++++++ system/include/{libc => compat}/sys/timeb.h | 0 system/include/compat/unistd.h | 16 ++++++++++++++++ system/include/compat/xlocale.h | 21 +++++++++++++++++++++ system/include/libc/stdlib.h | 2 -- system/include/libc/string.h | 2 -- system/include/libc/sys/unistd.h | 1 - system/include/xlocale.h | 7 +------ tools/shared.py | 1 + 12 files changed, 106 insertions(+), 11 deletions(-) create mode 100644 system/include/compat/math.h create mode 100644 system/include/compat/stdlib.h create mode 100644 system/include/compat/string.h create mode 100644 system/include/compat/sys/stat.h rename system/include/{libc => compat}/sys/timeb.h (100%) create mode 100644 system/include/compat/unistd.h create mode 100644 system/include/compat/xlocale.h diff --git a/system/include/compat/math.h b/system/include/compat/math.h new file mode 100644 index 0000000000000..089cf66b0155f --- /dev/null +++ b/system/include/compat/math.h @@ -0,0 +1,14 @@ +#ifndef _COMPAT_MATH_H_ +#define _COMPAT_MATH_H_ + +#ifndef isinff + #define isinff isinf +#endif + +#ifndef isnanf + #define isnanf isnan +#endif + +#include_next + +#endif /* _COMPAT_MATH_H_ */ diff --git a/system/include/compat/stdlib.h b/system/include/compat/stdlib.h new file mode 100644 index 0000000000000..dc01947d43aab --- /dev/null +++ b/system/include/compat/stdlib.h @@ -0,0 +1,16 @@ +#ifndef _COMPAT_STDLIB_H +#define _COMPAT_STDLIB_H + +#ifdef __cplusplus +extern "C" { +#endif + +int getloadavg(double loadavg[], int nelem); + +#ifdef __cplusplus +} +#endif + +#include_next + +#endif diff --git a/system/include/compat/string.h b/system/include/compat/string.h new file mode 100644 index 0000000000000..880089cc3bd03 --- /dev/null +++ b/system/include/compat/string.h @@ -0,0 +1,17 @@ +#ifndef _COMPAT_STRING_H +#define _COMPAT_STRING_H + +#ifdef __cplusplus +extern "C" { +#endif + +extern char* strlwr(char *); +extern char* strupr(char *); + +#ifdef __cplusplus +} +#endif + +#include_next + +#endif diff --git a/system/include/compat/sys/stat.h b/system/include/compat/sys/stat.h new file mode 100644 index 0000000000000..731502ea88efc --- /dev/null +++ b/system/include/compat/sys/stat.h @@ -0,0 +1,20 @@ +#ifndef _COMPAT_STAT_H +#define _COMPAT_STAT_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include_next + +#define S_IRWXUGO (S_IRWXU|S_IRWXG|S_IRWXO) +#define S_IALLUGO (S_ISUID|S_ISGID|S_ISVTX|S_IRWXUGO) +#define S_IRUGO (S_IRUSR|S_IRGRP|S_IROTH) +#define S_IWUGO (S_IWUSR|S_IWGRP|S_IWOTH) +#define S_IXUGO (S_IXUSR|S_IXGRP|S_IXOTH) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/system/include/libc/sys/timeb.h b/system/include/compat/sys/timeb.h similarity index 100% rename from system/include/libc/sys/timeb.h rename to system/include/compat/sys/timeb.h diff --git a/system/include/compat/unistd.h b/system/include/compat/unistd.h new file mode 100644 index 0000000000000..07c3afde1efdd --- /dev/null +++ b/system/include/compat/unistd.h @@ -0,0 +1,16 @@ +#ifndef _COMPAT_UNISTD_H +#define _COMPAT_UNISTD_H + +#ifdef __cplusplus +extern "C" { +#endif + +char * getwd(char *__buf ); + +#ifdef __cplusplus +} +#endif + +#include_next + +#endif diff --git a/system/include/compat/xlocale.h b/system/include/compat/xlocale.h new file mode 100644 index 0000000000000..4bafa27dd375d --- /dev/null +++ b/system/include/compat/xlocale.h @@ -0,0 +1,21 @@ +#ifndef _COMPAT_XLOCALE_H_ +#define _COMPAT_XLOCALE_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +long long strtoll_l(const char *start, char **end, int base, locale_t loc); +unsigned long long strtoull_l(const char *start, char **end, int base, locale_t loc); +double strtold_l(const char *start, char **end, locale_t loc); + +#ifdef __cplusplus +} +#endif + +#include_next + +#endif /* _COMPAT_XLOCALE_H_ */ + diff --git a/system/include/libc/stdlib.h b/system/include/libc/stdlib.h index 888b60411d483..6fdef40b46359 100644 --- a/system/include/libc/stdlib.h +++ b/system/include/libc/stdlib.h @@ -223,8 +223,6 @@ extern long double strtold (const char *, char **); extern long double wcstold (const wchar_t *, wchar_t **); #endif /* _LDBL_EQ_DBL */ -int getloadavg(double loadavg[], int nelem); /* XXX Emscripten */ - _END_STD_C #endif /* _STDLIB_H_ */ diff --git a/system/include/libc/string.h b/system/include/libc/string.h index 515c71a778a18..8fd9ea4331d19 100644 --- a/system/include/libc/string.h +++ b/system/include/libc/string.h @@ -72,8 +72,6 @@ size_t _EXFUN(strlcpy,(char *, const char *, size_t)); int _EXFUN(strncasecmp,(const char *, const char *, size_t)); size_t _EXFUN(strnlen,(const char *, size_t)); char *_EXFUN(strsep,(char **, const char *)); -char *_EXFUN(strlwr,(char *)); -char *_EXFUN(strupr,(char *)); #if defined(__CYGWIN__) || defined(EMSCRIPTEN) #ifndef DEFS_H /* Kludge to work around problem compiling in gdb */ char *_EXFUN(strsignal, (int __signo)); diff --git a/system/include/libc/sys/unistd.h b/system/include/libc/sys/unistd.h index 1a414b3c21cd0..a4219d4d26f1f 100644 --- a/system/include/libc/sys/unistd.h +++ b/system/include/libc/sys/unistd.h @@ -102,7 +102,6 @@ uid_t _EXFUN(getuid, (void )); #endif #if defined(EMSCRIPTEN) || defined(__CYGWIN__) char * _EXFUN(getusershell, (void)); -char * _EXFUN(getwd, (char *__buf )); int _EXFUN(iruserok, (unsigned long raddr, int superuser, const char *ruser, const char *luser)); #endif int _EXFUN(isatty, (int __fildes )); diff --git a/system/include/xlocale.h b/system/include/xlocale.h index bb3b50e5d9326..6867d25f7c181 100644 --- a/system/include/xlocale.h +++ b/system/include/xlocale.h @@ -1,4 +1,3 @@ - #ifndef _XLOCALE_H_ #define _XLOCALE_H_ @@ -9,10 +8,6 @@ extern "C" { #endif -long long strtoll_l(const char *start, char **end, int base, locale_t loc); -unsigned long long strtoull_l(const char *start, char **end, int base, locale_t loc); -double strtold_l(const char *start, char **end, locale_t loc); - int strcoll_l(const char *s1, const char *s2, locale_t locale); int wcscoll_l(const wchar_t *ws1, const wchar_t *ws2, locale_t locale); @@ -43,5 +38,5 @@ size_t strftime_l(char *s, size_t maxsize, const char *format, const struct tm * } #endif -#endif /* _LOCALE_H_ */ +#endif /* _XLOCALE_H_ */ diff --git a/tools/shared.py b/tools/shared.py index 917f548ed660d..0d0f20d4f4d2e 100644 --- a/tools/shared.py +++ b/tools/shared.py @@ -550,6 +550,7 @@ def get_llvm_target(): # allows projects to override them) EMSDK_OPTS = ['-nostdinc', '-Xclang', '-nobuiltininc', '-Xclang', '-nostdsysteminc', '-Xclang', '-isystem' + path_from_root('system', 'local', 'include'), + '-Xclang', '-isystem' + path_from_root('system', 'include', 'compat'), '-Xclang', '-isystem' + path_from_root('system', 'include', 'libcxx'), '-Xclang', '-isystem' + path_from_root('system', 'include'), '-Xclang', '-isystem' + path_from_root('system', 'include', 'emscripten'), From b02f43677abef35d8fe57a7a575fc143d9523742 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jukka=20Jyl=C3=A4nki?= Date: Fri, 16 Aug 2013 19:10:01 +0300 Subject: [PATCH 076/112] Fix test runner process exit code to report the number of failing tests, like it did before. --- tests/runner.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/runner.py b/tests/runner.py index 8feb83f9cb150..bdbd2676b3b1b 100755 --- a/tests/runner.py +++ b/tests/runner.py @@ -766,11 +766,17 @@ def btest(self, filename, expected=None, reference=None, force_c=False, referenc except: pass + numFailures = 0 # Keep count of the total number of failing tests. + # Run the discovered tests if not len(suites): print >> sys.stderr, 'No tests found for %s' % str(sys.argv[1:]) + numFailures = 1 else: testRunner = unittest.TextTestRunner(verbosity=2) for suite in suites: - testRunner.run(suite) + results = testRunner.run(suite) + numFailures += len(results.errors) + len(results.failures) + # Return the number of failures as the process exit code for automating success/failure reporting. + exit(numFailures) From 56d495e7ab8e999e2928dbc26b9fdedf0411590e Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 16 Aug 2013 10:08:01 -0700 Subject: [PATCH 077/112] convert gl emulation compilation errors to warnings and runtime errors --- src/library_gl.js | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/library_gl.js b/src/library_gl.js index 89a13bbe6ad6e..cd50a279859dc 100644 --- a/src/library_gl.js +++ b/src/library_gl.js @@ -4204,25 +4204,25 @@ var LibraryGL = { // Warn if code tries to use various emulation stuff, when emulation is disabled glVertexPointer__deps: [function() { - error('Legacy GL function (glVertexPointer) called. You need to compile with -s LEGACY_GL_EMULATION=1 to enable legacy GL emulation.'); + warn('Legacy GL function (glVertexPointer) called. You need to compile with -s LEGACY_GL_EMULATION=1 to enable legacy GL emulation.'); }], - glVertexPointer: function(){}, + glVertexPointer: function(){ throw 'Legacy GL function (glVertexPointer) called. You need to compile with -s LEGACY_GL_EMULATION=1 to enable legacy GL emulation.'; }, glGenVertexArrays__deps: [function() { - error('Legacy GL function (glGenVertexArrays) called. You need to compile with -s LEGACY_GL_EMULATION=1 to enable legacy GL emulation.'); + warn('Legacy GL function (glGenVertexArrays) called. You need to compile with -s LEGACY_GL_EMULATION=1 to enable legacy GL emulation.'); }], - glGenVertexArrays: function(){}, + glGenVertexArrays: function(){ throw 'Legacy GL function (glGenVertexArrays) called. You need to compile with -s LEGACY_GL_EMULATION=1 to enable legacy GL emulation.'; }, glMatrixMode__deps: [function() { - error('Legacy GL function (glMatrixMode) called. You need to compile with -s LEGACY_GL_EMULATION=1 to enable legacy GL emulation.'); + warn('Legacy GL function (glMatrixMode) called. You need to compile with -s LEGACY_GL_EMULATION=1 to enable legacy GL emulation.'); }], - glMatrixMode: function(){}, + glMatrixMode: function(){ throw 'Legacy GL function (glMatrixMode) called. You need to compile with -s LEGACY_GL_EMULATION=1 to enable legacy GL emulation.'; }, glBegin__deps: [function() { - error('Legacy GL function (glBegin) called. You need to compile with -s LEGACY_GL_EMULATION=1 to enable legacy GL emulation.'); + warn('Legacy GL function (glBegin) called. You need to compile with -s LEGACY_GL_EMULATION=1 to enable legacy GL emulation.'); }], - glBegin: function(){}, + glBegin: function(){ throw 'Legacy GL function (glBegin) called. You need to compile with -s LEGACY_GL_EMULATION=1 to enable legacy GL emulation.'; }, glLoadIdentity__deps: [function() { - error('Legacy GL function (glLoadIdentity) called. You need to compile with -s LEGACY_GL_EMULATION=1 to enable legacy GL emulation.'); + warn('Legacy GL function (glLoadIdentity) called. You need to compile with -s LEGACY_GL_EMULATION=1 to enable legacy GL emulation.'); }], - glLoadIdentity: function(){}, + glLoadIdentity: function(){ throw 'Legacy GL function (glLoadIdentity) called. You need to compile with -s LEGACY_GL_EMULATION=1 to enable legacy GL emulation.'; }, #endif // LEGACY_GL_EMULATION From a3acbcd3f0ab9ea5d1f25df9ec9ea4a8ce44f556 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 15 Aug 2013 18:18:50 -0700 Subject: [PATCH 078/112] pick up function indexing that only happens in pre --- emscripten.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/emscripten.py b/emscripten.py index 1b1284c71706e..a156ca73b098c 100755 --- a/emscripten.py +++ b/emscripten.py @@ -190,6 +190,11 @@ def save_settings(): open(forwarded_file, 'w').write(forwarded_data) if DEBUG: print >> sys.stderr, ' emscript: phase 1 took %s seconds' % (time.time() - t) + indexed_functions = set() + forwarded_json = json.loads(forwarded_data) + for key in forwarded_json['Functions']['indexedFunctions'].iterkeys(): + indexed_functions.add(key) + # Phase 2 - func cores = int(os.environ.get('EMCC_CORES') or multiprocessing.cpu_count()) @@ -203,8 +208,6 @@ def save_settings(): chunk_size = MAX_CHUNK_SIZE # if 1 core, just use the max chunk size if DEBUG: t = time.time() - forwarded_json = json.loads(forwarded_data) - indexed_functions = set() if settings.get('ASM_JS'): settings['EXPORTED_FUNCTIONS'] = forwarded_json['EXPORTED_FUNCTIONS'] save_settings() From 6250b3223a24701fe62bd56d1dd3422d3a372570 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 15 Aug 2013 18:21:34 -0700 Subject: [PATCH 079/112] simplify and automate GetProcAddress logic, and move it out of GL emulation --- src/jsifier.js | 9 +- src/library_gl.js | 216 ++++++++------------------------------------- src/library_sdl.js | 4 +- src/modules.js | 9 +- src/parseTools.js | 2 +- 5 files changed, 49 insertions(+), 191 deletions(-) diff --git a/src/jsifier.js b/src/jsifier.js index c92526d27f8a0..8ed191946756e 100644 --- a/src/jsifier.js +++ b/src/jsifier.js @@ -16,6 +16,8 @@ var SETJMP_LABEL = -1; var INDENTATION = ' '; +var functionStubSigs = {}; + // JSifier function JSify(data, functionsOnly, givenFunctions) { var mainPass = !functionsOnly; @@ -407,6 +409,11 @@ function JSify(data, functionsOnly, givenFunctions) { // functionStub substrate.addActor('FunctionStub', { processItem: function(item) { + // note the signature + if (item.returnType && item.params) { + functionStubSigs[item.ident] = Functions.getSignature(item.returnType.text, item.params.map(function(arg) { return arg.type }), false); + } + function addFromLibrary(ident) { if (ident in addedLibraryItems) return ''; addedLibraryItems[ident] = true; @@ -1537,7 +1544,7 @@ function JSify(data, functionsOnly, givenFunctions) { // This is a call through an invoke_*, either a forced one, or a setjmp-required one // note: no need to update argsTypes at this point if (byPointerForced) Functions.unimplementedFunctions[callIdent] = sig; - args.unshift(byPointerForced ? Functions.getIndex(callIdent, undefined, sig) : asmCoercion(callIdent, 'i32')); + args.unshift(byPointerForced ? Functions.getIndex(callIdent, sig) : asmCoercion(callIdent, 'i32')); callIdent = 'invoke_' + sig; } } else if (SAFE_DYNCALLS) { diff --git a/src/library_gl.js b/src/library_gl.js index cd50a279859dc..d69a479f9af78 100644 --- a/src/library_gl.js +++ b/src/library_gl.js @@ -1289,6 +1289,7 @@ var LibraryGL = { // GL emulation: provides misc. functionality not present in OpenGL ES 2.0 or WebGL + $GLEmulation__deps: ['$GLImmediateSetup', 'glEnable', 'glDisable', 'glIsEnabled', 'glGetBooleanv', 'glGetIntegerv', 'glGetString', 'glCreateShader', 'glShaderSource', 'glCompileShader', 'glAttachShader', 'glDetachShader', 'glUseProgram', 'glDeleteProgram', 'glBindAttribLocation', 'glLinkProgram', 'glBindBuffer', 'glGetFloatv', 'glHint', 'glEnableVertexAttribArray', 'glDisableVertexAttribArray', 'glVertexAttribPointer', 'glActiveTexture'], $GLEmulation__postset: 'GLEmulation.init();', $GLEmulation: { // Fog support. Partial, we assume shaders are used that implement fog. We just pass them uniforms @@ -1773,170 +1774,6 @@ var LibraryGL = { } return attrib; }, - - getProcAddress: function(name) { - name = name.replace('EXT', '').replace('ARB', ''); - // Do the translation carefully because of closure - var ret = 0; - switch (name) { - case 'glCreateShaderObject': case 'glCreateShader': ret = {{{ Functions.getIndex('_glCreateShader', true) }}}; break; - case 'glCreateProgramObject': case 'glCreateProgram': ret = {{{ Functions.getIndex('_glCreateProgram', true) }}}; break; - case 'glAttachObject': case 'glAttachShader': ret = {{{ Functions.getIndex('_glAttachShader', true) }}}; break; - case 'glUseProgramObject': case 'glUseProgram': ret = {{{ Functions.getIndex('_glUseProgram', true) }}}; break; - case 'glDetachObject': case 'glDetachShader': ret = {{{ Functions.getIndex('_glDetachShader', true) }}}; break; - case 'glDeleteObject': ret = {{{ Functions.getIndex('_glDeleteObject', true) }}}; break; - case 'glGetObjectParameteriv': ret = {{{ Functions.getIndex('_glGetObjectParameteriv', true) }}}; break; - case 'glGetInfoLog': ret = {{{ Functions.getIndex('_glGetInfoLog', true) }}}; break; - case 'glBindProgram': ret = {{{ Functions.getIndex('_glBindProgram', true) }}}; break; - case 'glDrawRangeElements': ret = {{{ Functions.getIndex('_glDrawRangeElements', true) }}}; break; - case 'glShaderSource': ret = {{{ Functions.getIndex('_glShaderSource', true) }}}; break; - case 'glCompileShader': ret = {{{ Functions.getIndex('_glCompileShader', true) }}}; break; - case 'glLinkProgram': ret = {{{ Functions.getIndex('_glLinkProgram', true) }}}; break; - case 'glGetUniformLocation': ret = {{{ Functions.getIndex('_glGetUniformLocation', true) }}}; break; - case 'glUniform1f': ret = {{{ Functions.getIndex('_glUniform1f', true) }}}; break; - case 'glUniform2f': ret = {{{ Functions.getIndex('_glUniform2f', true) }}}; break; - case 'glUniform3f': ret = {{{ Functions.getIndex('_glUniform3f', true) }}}; break; - case 'glUniform4f': ret = {{{ Functions.getIndex('_glUniform4f', true) }}}; break; - case 'glUniform1fv': ret = {{{ Functions.getIndex('_glUniform1fv', true) }}}; break; - case 'glUniform2fv': ret = {{{ Functions.getIndex('_glUniform2fv', true) }}}; break; - case 'glUniform3fv': ret = {{{ Functions.getIndex('_glUniform3fv', true) }}}; break; - case 'glUniform4fv': ret = {{{ Functions.getIndex('_glUniform4fv', true) }}}; break; - case 'glUniform1i': ret = {{{ Functions.getIndex('_glUniform1i', true) }}}; break; - case 'glUniform2i': ret = {{{ Functions.getIndex('_glUniform2i', true) }}}; break; - case 'glUniform3i': ret = {{{ Functions.getIndex('_glUniform3i', true) }}}; break; - case 'glUniform4i': ret = {{{ Functions.getIndex('_glUniform4i', true) }}}; break; - case 'glUniform1iv': ret = {{{ Functions.getIndex('_glUniform1iv', true) }}}; break; - case 'glUniform2iv': ret = {{{ Functions.getIndex('_glUniform2iv', true) }}}; break; - case 'glUniform3iv': ret = {{{ Functions.getIndex('_glUniform3iv', true) }}}; break; - case 'glUniform4iv': ret = {{{ Functions.getIndex('_glUniform4iv', true) }}}; break; - case 'glBindAttribLocation': ret = {{{ Functions.getIndex('_glBindAttribLocation', true) }}}; break; - case 'glGetActiveUniform': ret = {{{ Functions.getIndex('_glGetActiveUniform', true) }}}; break; - case 'glGenBuffers': ret = {{{ Functions.getIndex('_glGenBuffers', true) }}}; break; - case 'glBindBuffer': ret = {{{ Functions.getIndex('_glBindBuffer', true) }}}; break; - case 'glBufferData': ret = {{{ Functions.getIndex('_glBufferData', true) }}}; break; - case 'glBufferSubData': ret = {{{ Functions.getIndex('_glBufferSubData', true) }}}; break; - case 'glDeleteBuffers': ret = {{{ Functions.getIndex('_glDeleteBuffers', true) }}}; break; - case 'glActiveTexture': ret = {{{ Functions.getIndex('_glActiveTexture', true) }}}; break; - case 'glClientActiveTexture': ret = {{{ Functions.getIndex('_glClientActiveTexture', true) }}}; break; - case 'glGetProgramiv': ret = {{{ Functions.getIndex('_glGetProgramiv', true) }}}; break; - case 'glEnableVertexAttribArray': ret = {{{ Functions.getIndex('_glEnableVertexAttribArray', true) }}}; break; - case 'glDisableVertexAttribArray': ret = {{{ Functions.getIndex('_glDisableVertexAttribArray', true) }}}; break; - case 'glVertexAttribPointer': ret = {{{ Functions.getIndex('_glVertexAttribPointer', true) }}}; break; - case 'glVertexAttrib1f': ret = {{{ Functions.getIndex('_glVertexAttrib1f', true) }}}; break; - case 'glVertexAttrib2f': ret = {{{ Functions.getIndex('_glVertexAttrib2f', true) }}}; break; - case 'glVertexAttrib3f': ret = {{{ Functions.getIndex('_glVertexAttrib3f', true) }}}; break; - case 'glVertexAttrib4f': ret = {{{ Functions.getIndex('_glVertexAttrib4f', true) }}}; break; - case 'glVertexAttrib1fv': ret = {{{ Functions.getIndex('_glVertexAttrib1fv', true) }}}; break; - case 'glVertexAttrib2fv': ret = {{{ Functions.getIndex('_glVertexAttrib2fv', true) }}}; break; - case 'glVertexAttrib3fv': ret = {{{ Functions.getIndex('_glVertexAttrib3fv', true) }}}; break; - case 'glVertexAttrib4fv': ret = {{{ Functions.getIndex('_glVertexAttrib4fv', true) }}}; break; - case 'glGetVertexAttribfv': ret = {{{ Functions.getIndex('_glGetVertexAttribfv', true) }}}; break; - case 'glGetVertexAttribiv': ret = {{{ Functions.getIndex('_glGetVertexAttribiv', true) }}}; break; - case 'glGetVertexAttribPointerv': ret = {{{ Functions.getIndex('_glGetVertexAttribPointerv', true) }}}; break; - case 'glGetAttribLocation': ret = {{{ Functions.getIndex('_glGetAttribLocation', true) }}}; break; - case 'glGetActiveAttrib': ret = {{{ Functions.getIndex('_glGetActiveAttrib', true) }}}; break; - case 'glBindRenderbuffer': ret = {{{ Functions.getIndex('_glBindRenderbuffer', true) }}}; break; - case 'glDeleteRenderbuffers': ret = {{{ Functions.getIndex('_glDeleteRenderbuffers', true) }}}; break; - case 'glGenRenderbuffers': ret = {{{ Functions.getIndex('_glGenRenderbuffers', true) }}}; break; - case 'glCompressedTexImage2D': ret = {{{ Functions.getIndex('_glCompressedTexImage2D', true) }}}; break; - case 'glCompressedTexSubImage2D': ret = {{{ Functions.getIndex('_glCompressedTexSubImage2D', true) }}}; break; - case 'glBindFramebuffer': ret = {{{ Functions.getIndex('_glBindFramebuffer', true) }}}; break; - case 'glGenFramebuffers': ret = {{{ Functions.getIndex('_glGenFramebuffers', true) }}}; break; - case 'glDeleteFramebuffers': ret = {{{ Functions.getIndex('_glDeleteFramebuffers', true) }}}; break; - case 'glFramebufferRenderbuffer': ret = {{{ Functions.getIndex('_glFramebufferRenderbuffer', true) }}}; break; - case 'glFramebufferTexture2D': ret = {{{ Functions.getIndex('_glFramebufferTexture2D', true) }}}; break; - case 'glGetFramebufferAttachmentParameteriv': ret = {{{ Functions.getIndex('_glGetFramebufferAttachmentParameteriv', true) }}}; break; - case 'glIsFramebuffer': ret = {{{ Functions.getIndex('_glIsFramebuffer', true) }}}; break; - case 'glCheckFramebufferStatus': ret = {{{ Functions.getIndex('_glCheckFramebufferStatus', true) }}}; break; - case 'glRenderbufferStorage': ret = {{{ Functions.getIndex('_glRenderbufferStorage', true) }}}; break; - case 'glGenVertexArrays': ret = {{{ Functions.getIndex('_glGenVertexArrays', true) }}}; break; - case 'glDeleteVertexArrays': ret = {{{ Functions.getIndex('_glDeleteVertexArrays', true) }}}; break; - case 'glBindVertexArray': ret = {{{ Functions.getIndex('_glBindVertexArray', true) }}}; break; - case 'glGetString': ret = {{{ Functions.getIndex('_glGetString', true) }}}; break; - case 'glBindTexture': ret = {{{ Functions.getIndex('_glBindTexture', true) }}}; break; - case 'glGetBufferParameteriv': ret = {{{ Functions.getIndex('_glGetBufferParameteriv', true) }}}; break; - case 'glIsBuffer': ret = {{{ Functions.getIndex('_glIsBuffer', true) }}}; break; - case 'glDeleteShader': ret = {{{ Functions.getIndex('_glDeleteShader', true) }}}; break; - case 'glUniformMatrix2fv': ret = {{{ Functions.getIndex('_glUniformMatrix2fv', true) }}}; break; - case 'glUniformMatrix3fv': ret = {{{ Functions.getIndex('_glUniformMatrix3fv', true) }}}; break; - case 'glUniformMatrix4fv': ret = {{{ Functions.getIndex('_glUniformMatrix4fv', true) }}}; break; - case 'glIsRenderbuffer': ret = {{{ Functions.getIndex('_glIsRenderbuffer', true) }}}; break; - case 'glBlendEquation': ret = {{{ Functions.getIndex('_glBlendEquation', true) }}}; break; - case 'glBlendFunc': ret = {{{ Functions.getIndex('_glBlendFunc', true) }}}; break; - case 'glBlendFuncSeparate': ret = {{{ Functions.getIndex('_glBlendFuncSeparate', true) }}}; break; - case 'glBlendEquationSeparate': ret = {{{ Functions.getIndex('_glBlendEquationSeparate', true) }}}; break; - case 'glDepthRangef': ret = {{{ Functions.getIndex('_glDepthRangef', true) }}}; break; - case 'glClear': ret = {{{ Functions.getIndex('_glClear', true) }}}; break; - case 'glGenerateMipmap': ret = {{{ Functions.getIndex('_glGenerateMipmap', true) }}}; break; - case 'glBlendColor': ret = {{{ Functions.getIndex('_glBlendColor', true) }}}; break; - case 'glClearDepthf': ret = {{{ Functions.getIndex('_glClearDepthf', true) }}}; break; - case 'glDeleteProgram': ret = {{{ Functions.getIndex('_glDeleteProgram', true) }}}; break; - case 'glUniformMatrix3fv': ret = {{{ Functions.getIndex('_glUniformMatrix3fv', true) }}}; break; - case 'glClearColor': ret = {{{ Functions.getIndex('_glClearColor', true) }}}; break; - case 'glGetRenderbufferParameteriv': ret = {{{ Functions.getIndex('_glGetRenderbufferParameteriv', true) }}}; break; - case 'glGetShaderInfoLog': ret = {{{ Functions.getIndex('_glGetShaderInfoLog', true) }}}; break; - case 'glUniformMatrix4fv': ret = {{{ Functions.getIndex('_glUniformMatrix4fv', true) }}}; break; - case 'glClearStencil': ret = {{{ Functions.getIndex('_glClearStencil', true) }}}; break; - case 'glGetProgramInfoLog': ret = {{{ Functions.getIndex('_glGetProgramInfoLog', true) }}}; break; - case 'glGetUniformfv': ret = {{{ Functions.getIndex('_glGetUniformfv', true) }}}; break; - case 'glStencilFuncSeparate': ret = {{{ Functions.getIndex('_glStencilFuncSeparate', true) }}}; break; - case 'glSampleCoverage': ret = {{{ Functions.getIndex('_glSampleCoverage', true) }}}; break; - case 'glColorMask': ret = {{{ Functions.getIndex('_glColorMask', true) }}}; break; - case 'glGetShaderiv': ret = {{{ Functions.getIndex('_glGetShaderiv', true) }}}; break; - case 'glGetUniformiv': ret = {{{ Functions.getIndex('_glGetUniformiv', true) }}}; break; - case 'glCopyTexSubImage2D': ret = {{{ Functions.getIndex('_glCopyTexSubImage2D', true) }}}; break; - case 'glDetachShader': ret = {{{ Functions.getIndex('_glDetachShader', true) }}}; break; - case 'glGetShaderSource': ret = {{{ Functions.getIndex('_glGetShaderSource', true) }}}; break; - case 'glDeleteTextures': ret = {{{ Functions.getIndex('_glDeleteTextures', true) }}}; break; - case 'glGetAttachedShaders': ret = {{{ Functions.getIndex('_glGetAttachedShaders', true) }}}; break; - case 'glValidateProgram': ret = {{{ Functions.getIndex('_glValidateProgram', true) }}}; break; - case 'glDepthFunc': ret = {{{ Functions.getIndex('_glDepthFunc', true) }}}; break; - case 'glIsShader': ret = {{{ Functions.getIndex('_glIsShader', true) }}}; break; - case 'glDepthMask': ret = {{{ Functions.getIndex('_glDepthMask', true) }}}; break; - case 'glStencilMaskSeparate': ret = {{{ Functions.getIndex('_glStencilMaskSeparate', true) }}}; break; - case 'glIsProgram': ret = {{{ Functions.getIndex('_glIsProgram', true) }}}; break; - case 'glDisable': ret = {{{ Functions.getIndex('_glDisable', true) }}}; break; - case 'glStencilOpSeparate': ret = {{{ Functions.getIndex('_glStencilOpSeparate', true) }}}; break; - case 'glDrawArrays': ret = {{{ Functions.getIndex('_glDrawArrays', true) }}}; break; - case 'glDrawElements': ret = {{{ Functions.getIndex('_glDrawElements', true) }}}; break; - case 'glEnable': ret = {{{ Functions.getIndex('_glEnable', true) }}}; break; - case 'glFinish': ret = {{{ Functions.getIndex('_glFinish', true) }}}; break; - case 'glFlush': ret = {{{ Functions.getIndex('_glFlush', true) }}}; break; - case 'glFrontFace': ret = {{{ Functions.getIndex('_glFrontFace', true) }}}; break; - case 'glCullFace': ret = {{{ Functions.getIndex('_glCullFace', true) }}}; break; - case 'glGenTextures': ret = {{{ Functions.getIndex('_glGenTextures', true) }}}; break; - case 'glGetError': ret = {{{ Functions.getIndex('_glGetError', true) }}}; break; - case 'glGetIntegerv': ret = {{{ Functions.getIndex('_glGetIntegerv', true) }}}; break; - case 'glGetBooleanv': ret = {{{ Functions.getIndex('_glGetBooleanv', true) }}}; break; - case 'glGetFloatv': ret = {{{ Functions.getIndex('_glGetFloatv', true) }}}; break; - case 'glHint': ret = {{{ Functions.getIndex('_glHint', true) }}}; break; - case 'glIsTexture': ret = {{{ Functions.getIndex('_glIsTexture', true) }}}; break; - case 'glPixelStorei': ret = {{{ Functions.getIndex('_glPixelStorei', true) }}}; break; - case 'glReadPixels': ret = {{{ Functions.getIndex('_glReadPixels', true) }}}; break; - case 'glScissor': ret = {{{ Functions.getIndex('_glScissor', true) }}}; break; - case 'glStencilFunc': ret = {{{ Functions.getIndex('_glStencilFunc', true) }}}; break; - case 'glStencilMask': ret = {{{ Functions.getIndex('_glStencilMask', true) }}}; break; - case 'glStencilOp': ret = {{{ Functions.getIndex('_glStencilOp', true) }}}; break; - case 'glTexImage2D': ret = {{{ Functions.getIndex('_glTexImage2D', true) }}}; break; - case 'glTexParameterf': ret = {{{ Functions.getIndex('_glTexParameterf', true) }}}; break; - case 'glTexParameterfv': ret = {{{ Functions.getIndex('_glTexParameterfv', true) }}}; break; - case 'glTexParameteri': ret = {{{ Functions.getIndex('_glTexParameteri', true) }}}; break; - case 'glTexParameteriv': ret = {{{ Functions.getIndex('_glTexParameteriv', true) }}}; break; - case 'glGetTexParameterfv': ret = {{{ Functions.getIndex('_glGetTexParameterfv', true) }}}; break; - case 'glGetTexParameteriv': ret = {{{ Functions.getIndex('_glGetTexParameteriv', true) }}}; break; - case 'glTexSubImage2D': ret = {{{ Functions.getIndex('_glTexSubImage2D', true) }}}; break; - case 'glCopyTexImage2D': ret = {{{ Functions.getIndex('_glCopyTexImage2D', true) }}}; break; - case 'glViewport': ret = {{{ Functions.getIndex('_glViewport', true) }}}; break; - case 'glIsEnabled': ret = {{{ Functions.getIndex('_glIsEnabled', true) }}}; break; - case 'glLineWidth': ret = {{{ Functions.getIndex('_glLineWidth', true) }}}; break; - case 'glPolygonOffset': ret = {{{ Functions.getIndex('_glPolygonOffset', true) }}}; break; - case 'glReleaseShaderCompiler': ret = {{{ Functions.getIndex('_glReleaseShaderCompiler', true) }}}; break; - case 'glGetShaderPrecisionFormat': ret = {{{ Functions.getIndex('_glGetShaderPrecisionFormat', true) }}}; break; - case 'glShaderBinary': ret = {{{ Functions.getIndex('_glShaderBinary', true) }}}; break; - } - if (!ret) Module.printErr('WARNING: getProcAddress failed for ' + name); - return ret; - } }, glGetShaderPrecisionFormat__sig: 'v', @@ -4460,27 +4297,46 @@ var LibraryGL = { autoAddDeps(LibraryGL, '$GL'); +// Legacy GL emulation if (LEGACY_GL_EMULATION) { - // Emulation requires everything else, potentially - LibraryGL.$GLEmulation__deps = LibraryGL.$GLEmulation__deps.slice(0); // the __deps object is shared - var glFuncs = []; - for (var item in LibraryGL) { - if (item != '$GLEmulation' && item.substr(-6) != '__deps' && item.substr(-9) != '__postset' && item.substr(-5) != '__sig' && item.substr(0, 2) == 'gl') { - glFuncs.push(item); - } - } - LibraryGL.$GLEmulation__deps = LibraryGL.$GLEmulation__deps.concat(glFuncs); - LibraryGL.$GLEmulation__deps.push(function() { - for (var func in Functions.getIndex.tentative) { - Functions.getIndex(func); - Functions.unimplementedFunctions[func] = LibraryGL[func.substr(1) + '__sig']; + DEFAULT_LIBRARY_FUNCS_TO_INCLUDE.push('$GLEmulation'); +} + +// GL proc address retrieval +LibraryGL.emscripten_GetProcAddress__deps = [function() { + // ProcAddress is used, so include everything in GL. This runs before we go to the $ProcAddressTable object, + // and we fill its deps just in time, and create the lookup table + var table = {}; + LibraryManager.library.emscripten_procAddressTable__deps = keys(LibraryGL).filter(function(x) { + if (x.substr(-6) == '__deps' || x.substr(-9) == '__postset' || x.substr(-5) == '__sig' || x.substr(0, 2) != 'gl') return false; + var sig = LibraryGL[x + '__sig'] || functionStubSigs['_' + x]; + if (sig) { + table[x] = Functions.getIndex('_' + x, sig); + if (!(('_' + x) in Functions.implementedFunctions)) Functions.unimplementedFunctions[('_' + x)] = sig; } + return true; }); - - LibraryGL.glDrawElements__deps = LibraryGL.glDrawElements__deps.concat('$GLEmulation'); - LibraryGL.glDrawArrays__deps = LibraryGL.glDrawArrays__deps.concat('$GLEmulation'); + // convert table into function with switch, to not confuse closure compiler + var tableImpl = 'switch(name) {\n'; + for (var x in table) tableImpl += 'case "' + x + '": return ' + table[x] + '; break;\n'; + tableImpl += '}\nreturn 0;'; + LibraryManager.library.emscripten_procAddressTable = new Function('name', tableImpl); +}, 'emscripten_procAddressTable']; +LibraryGL.emscripten_GetProcAddress = function(name) { + name = name.replace('EXT', '').replace('ARB', ''); + switch(name) { // misc renamings + case 'glCreateProgramObject': name = 'glCreateProgram'; break; + case 'glUseProgramObject': name = 'glUseProgram'; break; + case 'glCreateShaderObject': name = 'glCreateShader'; break; + case 'glAttachObject': name = 'glAttachShader'; break; + case 'glDetachObject': name = 'glDetachShader'; break; + } + var ret = _emscripten_procAddressTable(name); + if (!ret) Module.printErr('WARNING: getProcAddress failed for ' + name); + return ret; } +// Final merge mergeInto(LibraryManager.library, LibraryGL); assert(!(FULL_ES2 && LEGACY_GL_EMULATION), 'cannot emulate both ES2 and legacy GL'); diff --git a/src/library_sdl.js b/src/library_sdl.js index 6cc337e8f22f3..236ab1bf984bf 100644 --- a/src/library_sdl.js +++ b/src/library_sdl.js @@ -2067,9 +2067,9 @@ var LibrarySDL = { console.log('TODO: SDL_GL_SetAttribute'); }, - SDL_GL_GetProcAddress__deps: ['$GLEmulation'], + SDL_GL_GetProcAddress__deps: ['emscripten_GetProcAddress'], SDL_GL_GetProcAddress: function(name_) { - return GLEmulation.getProcAddress(Pointer_stringify(name_)); + return _emscripten_GetProcAddress(Pointer_stringify(name_)); }, SDL_GL_SwapBuffers: function() {}, diff --git a/src/modules.js b/src/modules.js index 135617772f8ba..01f0d8ae88cc3 100644 --- a/src/modules.js +++ b/src/modules.js @@ -275,20 +275,15 @@ var Functions = { }, // Mark a function as needing indexing. Python will coordinate them all - getIndex: function(ident, doNotCreate, sig) { - if (doNotCreate && !(ident in this.indexedFunctions)) { - if (!Functions.getIndex.tentative) Functions.getIndex.tentative = {}; // only used by GL emulation; TODO: generalize when needed - Functions.getIndex.tentative[ident] = 0; - } + getIndex: function(ident, sig) { var ret; if (phase != 'post' && singlePhase) { - if (!doNotCreate) this.indexedFunctions[ident] = 0; // tell python we need this indexized ret = "'{{ FI_" + toNiceIdent(ident) + " }}'"; // something python will replace later + this.indexedFunctions[ident] = 0; } else { if (!singlePhase) return 'NO_INDEX'; // Should not index functions in post ret = this.indexedFunctions[ident]; if (!ret) { - if (doNotCreate) return '0'; ret = this.nextIndex; this.nextIndex += 2; // Need to have indexes be even numbers, see |polymorph| test this.indexedFunctions[ident] = ret; diff --git a/src/parseTools.js b/src/parseTools.js index f11c867a53bbc..65e962648ebc5 100644 --- a/src/parseTools.js +++ b/src/parseTools.js @@ -1244,7 +1244,7 @@ function indexizeFunctions(value, type) { if (!sig) { sig = Functions.unimplementedFunctions[value] = Functions.getSignature(out.returnType, out.segments ? out.segments.map(function(segment) { return segment[0].text }) : [], isVarArgsFunctionType(type)); } - return Functions.getIndex(value, undefined, sig); + return Functions.getIndex(value, sig); } return value; } From 52f2d45d070342b82dee9fcac325b8535f3adc65 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 16 Aug 2013 10:46:02 -0700 Subject: [PATCH 080/112] do not warn on gl emulation code if INCLUDE_FULL_LIBRARY is set, only error at runtime --- src/library_gl.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/library_gl.js b/src/library_gl.js index d69a479f9af78..dc1c1fc641717 100644 --- a/src/library_gl.js +++ b/src/library_gl.js @@ -4040,24 +4040,36 @@ var LibraryGL = { #else // LEGACY_GL_EMULATION // Warn if code tries to use various emulation stuff, when emulation is disabled + // (do not warn if INCLUDE_FULL_LIBRARY is one, because then likely the gl code will + // not be called anyhow, leave only the runtime aborts) glVertexPointer__deps: [function() { +#if INCLUDE_FULL_LIBRARY == 0 warn('Legacy GL function (glVertexPointer) called. You need to compile with -s LEGACY_GL_EMULATION=1 to enable legacy GL emulation.'); +#endif }], glVertexPointer: function(){ throw 'Legacy GL function (glVertexPointer) called. You need to compile with -s LEGACY_GL_EMULATION=1 to enable legacy GL emulation.'; }, glGenVertexArrays__deps: [function() { +#if INCLUDE_FULL_LIBRARY == 0 warn('Legacy GL function (glGenVertexArrays) called. You need to compile with -s LEGACY_GL_EMULATION=1 to enable legacy GL emulation.'); +#endif }], glGenVertexArrays: function(){ throw 'Legacy GL function (glGenVertexArrays) called. You need to compile with -s LEGACY_GL_EMULATION=1 to enable legacy GL emulation.'; }, glMatrixMode__deps: [function() { +#if INCLUDE_FULL_LIBRARY == 0 warn('Legacy GL function (glMatrixMode) called. You need to compile with -s LEGACY_GL_EMULATION=1 to enable legacy GL emulation.'); +#endif }], glMatrixMode: function(){ throw 'Legacy GL function (glMatrixMode) called. You need to compile with -s LEGACY_GL_EMULATION=1 to enable legacy GL emulation.'; }, glBegin__deps: [function() { +#if INCLUDE_FULL_LIBRARY == 0 warn('Legacy GL function (glBegin) called. You need to compile with -s LEGACY_GL_EMULATION=1 to enable legacy GL emulation.'); +#endif }], glBegin: function(){ throw 'Legacy GL function (glBegin) called. You need to compile with -s LEGACY_GL_EMULATION=1 to enable legacy GL emulation.'; }, glLoadIdentity__deps: [function() { +#if INCLUDE_FULL_LIBRARY == 0 warn('Legacy GL function (glLoadIdentity) called. You need to compile with -s LEGACY_GL_EMULATION=1 to enable legacy GL emulation.'); +#endif }], glLoadIdentity: function(){ throw 'Legacy GL function (glLoadIdentity) called. You need to compile with -s LEGACY_GL_EMULATION=1 to enable legacy GL emulation.'; }, From ab6452296324f9827b07780c90847a69f2173ed8 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 16 Aug 2013 11:39:04 -0700 Subject: [PATCH 081/112] generate separate function table wrappers for a single library function aliased with different signatures --- src/modules.js | 9 +++++---- tests/aniso.c | 10 ++++++++-- tests/test_browser.py | 6 ++++++ 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/modules.js b/src/modules.js index 01f0d8ae88cc3..fa6c0983987c5 100644 --- a/src/modules.js +++ b/src/modules.js @@ -321,7 +321,7 @@ var Functions = { tables[sig][index] = ident; } var generated = false; - var wrapped = {}; + var wrapped = {}; // whether we wrapped a lib func var maxTable = 0; for (var t in tables) { if (t == 'pre') continue; @@ -344,10 +344,11 @@ var Functions = { if (ASM_JS) { var curr = table[i]; if (curr && curr != '0' && !Functions.implementedFunctions[curr]) { - curr = toNiceIdent(curr); // fix Math.* to Math_* + var short = toNiceIdent(curr); // fix Math.* to Math_* + curr = t + '_' + short; // libfuncs can alias with different sigs, wrap each separately // This is a library function, we can't just put it in the function table, need a wrapper if (!wrapped[curr]) { - var args = '', arg_coercions = '', call = curr + '(', retPre = '', retPost = ''; + var args = '', arg_coercions = '', call = short + '(', retPre = '', retPost = ''; if (t[0] != 'v') { if (t[0] == 'i') { retPre = 'return '; @@ -362,7 +363,7 @@ var Functions = { call += (j > 1 ? ',' : '') + asmCoercion('a' + j, t[j] != 'i' ? 'float' : 'i32'); } call += ')'; - if (curr == '_setjmp') printErr('WARNING: setjmp used via a function pointer. If this is for libc setjmp (not something of your own with the same name), it will break things'); + if (short == '_setjmp') printErr('WARNING: setjmp used via a function pointer. If this is for libc setjmp (not something of your own with the same name), it will break things'); tables.pre += 'function ' + curr + '__wrapper(' + args + ') { ' + arg_coercions + ' ; ' + retPre + call + retPost + ' }\n'; wrapped[curr] = 1; } diff --git a/tests/aniso.c b/tests/aniso.c index 1126265e5ab7f..f210e5a52d3f2 100644 --- a/tests/aniso.c +++ b/tests/aniso.c @@ -161,7 +161,7 @@ int main(int argc, char *argv[]) for (int x = 0; x < n; x++) { int start = x*w*2; glBegin( GL_TRIANGLES ); - glTexCoord2i( 1, 0 ); glVertex3f( start , 0, 0 ); + glTexCoord2i( 1, 0 ); glVertex2i( start , 0 ); glTexCoord2i( 0, 0 ); glVertex3f( start+w, 300, 0 ); glTexCoord2i( 1, 1 ); glVertex3f( start-w, 300, 0 ); glEnd(); @@ -209,5 +209,11 @@ int main(int argc, char *argv[]) SDL_Quit(); - return 0; + // check for asm compilation bug with aliased functions with different sigs + void (*f)(int, int) = glVertex2i; + if ((int)f % 16 == 4) f(5, 7); + void (*g)(int, int) = glVertex3f; + if ((int)g % 16 == 4) g(5, 7); + return (int)f + (int)g; } + diff --git a/tests/test_browser.py b/tests/test_browser.py index cd5668a301e27..323677baeff9f 100644 --- a/tests/test_browser.py +++ b/tests/test_browser.py @@ -1323,6 +1323,12 @@ def test_s3tc_crunch_split(self): # load several datafiles/outputs of file packa self.btest('s3tc_crunch.c', reference='s3tc_crunch.png', reference_slack=11, args=['--pre-js', 'asset_a.js', '--pre-js', 'asset_b.js', '-s', 'LEGACY_GL_EMULATION=1']) def test_aniso(self): + if SPIDERMONKEY_ENGINE in JS_ENGINES: + # asm.js-ification check + Popen([PYTHON, EMCC, path_from_root('tests', 'aniso.c'), '-O2', '-g2', '-s', 'LEGACY_GL_EMULATION=1']).communicate() + Settings.ASM_JS = 1 + self.run_generated_code(SPIDERMONKEY_ENGINE, 'a.out.js') + shutil.copyfile(path_from_root('tests', 'water.dds'), 'water.dds') self.btest('aniso.c', reference='aniso.png', reference_slack=2, args=['--preload-file', 'water.dds', '-s', 'LEGACY_GL_EMULATION=1']) From f46ea8746cd469b8ba664e52a63b6112fcd48b9e Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 16 Aug 2013 11:48:42 -0700 Subject: [PATCH 082/112] warn on VERBOSE --- src/compiler.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/compiler.js b/src/compiler.js index 365ff32fb8469..2390f4c9eba5f 100644 --- a/src/compiler.js +++ b/src/compiler.js @@ -203,6 +203,8 @@ if (phase == 'pre') { } } +if (VERBOSE) printErr('VERBOSE is on, this generates a lot of output and can slow down compilation'); + // Load compiler code load('framework.js'); From 1d89260748bcf2ba5965ffa67126f3edaef31f07 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 16 Aug 2013 12:58:13 -0700 Subject: [PATCH 083/112] allow overriding a gl function with the same name, and keep the original accessible through getProcAddress --- src/library_gl.js | 27 ++++-- tests/sdl_ogl_proc_alias.c | 180 +++++++++++++++++++++++++++++++++++++ tests/test_browser.py | 5 ++ 3 files changed, 205 insertions(+), 7 deletions(-) create mode 100644 tests/sdl_ogl_proc_alias.c diff --git a/src/library_gl.js b/src/library_gl.js index dc1c1fc641717..c134ad97ff21b 100644 --- a/src/library_gl.js +++ b/src/library_gl.js @@ -4319,15 +4319,28 @@ LibraryGL.emscripten_GetProcAddress__deps = [function() { // ProcAddress is used, so include everything in GL. This runs before we go to the $ProcAddressTable object, // and we fill its deps just in time, and create the lookup table var table = {}; - LibraryManager.library.emscripten_procAddressTable__deps = keys(LibraryGL).filter(function(x) { - if (x.substr(-6) == '__deps' || x.substr(-9) == '__postset' || x.substr(-5) == '__sig' || x.substr(0, 2) != 'gl') return false; - var sig = LibraryGL[x + '__sig'] || functionStubSigs['_' + x]; + LibraryManager.library.emscripten_procAddressTable__deps = keys(LibraryGL).map(function(x) { + if (x.substr(-6) == '__deps' || x.substr(-9) == '__postset' || x.substr(-5) == '__sig' || x.substr(-5) == '__asm' || x.substr(0, 2) != 'gl') return null; + var original = x; + if (('_' + x) in Functions.implementedFunctions) { + // a user-implemented function aliases this one, but we still want it to be accessible by name, so rename it + var y = x + '__procTable'; + LibraryManager.library[y] = LibraryManager.library[x]; + LibraryManager.library[y + '__deps'] = LibraryManager.library[x + '__deps']; + LibraryManager.library[y + '__postset'] = LibraryManager.library[x + '__postset']; + LibraryManager.library[y + '__sig'] = LibraryManager.library[x + '__sig'];//|| Functions.implementedFunctions['_' + x]; + LibraryManager.library[y + '__asm'] = LibraryManager.library[x + '__asm']; + x = y; + assert(!(y in Functions.implementedFunctions) && !Functions.unimplementedFunctions['_' + y]); + } + var longX = '_' + x; + var sig = LibraryManager.library[x + '__sig'] || functionStubSigs[longX]; if (sig) { - table[x] = Functions.getIndex('_' + x, sig); - if (!(('_' + x) in Functions.implementedFunctions)) Functions.unimplementedFunctions[('_' + x)] = sig; + table[original] = Functions.getIndex(longX, sig); + if (!(longX in Functions.implementedFunctions)) Functions.unimplementedFunctions[longX] = sig; } - return true; - }); + return x; + }).filter(function(x) { return x !== null }); // convert table into function with switch, to not confuse closure compiler var tableImpl = 'switch(name) {\n'; for (var x in table) tableImpl += 'case "' + x + '": return ' + table[x] + '; break;\n'; diff --git a/tests/sdl_ogl_proc_alias.c b/tests/sdl_ogl_proc_alias.c new file mode 100644 index 0000000000000..c96da81b651ed --- /dev/null +++ b/tests/sdl_ogl_proc_alias.c @@ -0,0 +1,180 @@ +/******************************************************************* + * * + * Using SDL With OpenGL * + * * + * Tutorial by Kyle Foley (sdw) * + * * + * http://gpwiki.org/index.php/SDL:Tutorials:Using_SDL_with_OpenGL * + * * + *******************************************************************/ + +/* +THIS WORK, INCLUDING THE SOURCE CODE, DOCUMENTATION +AND RELATED MEDIA AND DATA, IS PLACED INTO THE PUBLIC DOMAIN. + +THE ORIGINAL AUTHOR IS KYLE FOLEY. + +THIS SOFTWARE IS PROVIDED AS-IS WITHOUT WARRANTY +OF ANY KIND, NOT EVEN THE IMPLIED WARRANTY OF +MERCHANTABILITY. THE AUTHOR OF THIS SOFTWARE, +ASSUMES _NO_ RESPONSIBILITY FOR ANY CONSEQUENCE +RESULTING FROM THE USE, MODIFICATION, OR +REDISTRIBUTION OF THIS SOFTWARE. +*/ + +#include "SDL/SDL.h" +#include "SDL/SDL_image.h" +#include "SDL/SDL_opengl.h" + +#include +#include + +void (*true_glGenTextures)(GLsizei, GLuint*) = NULL; + +void glGenTextures(GLsizei n, GLuint *textures) { + printf("num? %d\n", n); + true_glGenTextures(n + 1, textures); // correct the error, ensures we are gone through +} + +int main(int argc, char *argv[]) +{ + SDL_Surface *screen; + + // Slightly different SDL initialization + if ( SDL_Init(SDL_INIT_VIDEO) != 0 ) { + printf("Unable to initialize SDL: %s\n", SDL_GetError()); + return 1; + } + + SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 ); // *new* + + screen = SDL_SetVideoMode( 640, 480, 16, SDL_OPENGL ); // *changed* + if ( !screen ) { + printf("Unable to set video mode: %s\n", SDL_GetError()); + return 1; + } + + // Set the OpenGL state after creating the context with SDL_SetVideoMode + + glClearColor( 0, 0, 0, 0 ); + + glEnable( GL_TEXTURE_2D ); // Needed when we're using the fixed-function pipeline. + + glViewport( 0, 0, 640, 480 ); + + glMatrixMode( GL_PROJECTION ); + glPushMatrix(); // just for testing + glLoadIdentity(); + + glOrtho( 0, 640, 480, 0, -1, 1 ); + + glMatrixMode( GL_MODELVIEW ); + glLoadIdentity(); + + // Load the OpenGL texture + + GLuint texture; // Texture object handle + SDL_Surface *surface; // Gives us the information to make the texture + + if ( (surface = IMG_Load("screenshot.png")) ) { + + // Check that the image's width is a power of 2 + if ( (surface->w & (surface->w - 1)) != 0 ) { + printf("warning: image.bmp's width is not a power of 2\n"); + } + + // Also check if the height is a power of 2 + if ( (surface->h & (surface->h - 1)) != 0 ) { + printf("warning: image.bmp's height is not a power of 2\n"); + } + + true_glGenTextures = SDL_GL_GetProcAddress("glGenTextures"); + + // Have OpenGL generate a texture object handle for us + glGenTextures( 0, &texture ); + + // Bind the texture object + glBindTexture( GL_TEXTURE_2D, texture ); + + // Set the texture's stretching properties + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + + //SDL_LockSurface(surface); + + // Add some greyness + memset(surface->pixels, 0x66, surface->w*surface->h); + + // Edit the texture object's image data using the information SDL_Surface gives us + glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, surface->w, surface->h, 0, + GL_RGBA, GL_UNSIGNED_BYTE, surface->pixels ); + + //SDL_UnlockSurface(surface); + } + else { + printf("SDL could not load image.bmp: %s\n", SDL_GetError()); + SDL_Quit(); + return 1; + } + + // Free the SDL_Surface only if it was successfully created + if ( surface ) { + SDL_FreeSurface( surface ); + } + + // Clear the screen before drawing + glClear( GL_COLOR_BUFFER_BIT ); + + // Bind the texture to which subsequent calls refer to + glBindTexture( GL_TEXTURE_2D, texture ); + + glBegin( GL_QUADS ); + glTexCoord2i( 0, 0 ); glVertex3f( 10, 10, 0 ); + glTexCoord2i( 1, 0 ); glVertex3f( 300, 10, 0 ); + glTexCoord2i( 1, 1 ); glVertex3f( 300, 128, 0 ); + glTexCoord2i( 0, 1 ); glVertex3f( 10, 128, 0 ); + + glTexCoord2f( 0, 0.5 ); glVertex3f( 410, 10, 0 ); + glTexCoord2f( 1, 0.5 ); glVertex3f( 600, 10, 0 ); + glTexCoord2f( 1, 1 ); glVertex3f( 630, 200, 0 ); + glTexCoord2f( 0.5, 1 ); glVertex3f( 310, 250, 0 ); + glEnd(); + + glBegin( GL_TRIANGLE_STRIP ); + glTexCoord2i( 0, 0 ); glVertex3f( 100, 300, 0 ); + glTexCoord2i( 1, 0 ); glVertex3f( 300, 300, 0 ); + glTexCoord2i( 1, 1 ); glVertex3f( 300, 400, 0 ); + glTexCoord2i( 0, 1 ); glVertex3f( 500, 410, 0 ); + glEnd(); + + glDisable(GL_TEXTURE_2D); + + glColor3ub(90, 255, 255); + glBegin( GL_QUADS ); + glVertex3f( 10, 410, 0 ); + glVertex3f( 300, 410, 0 ); + glVertex3f( 300, 480, 0 ); + glVertex3f( 10, 470, 0 ); + glEnd(); + + glBegin( GL_QUADS ); + glColor3f(1.0, 0, 1.0); glVertex3f( 410, 410, 0 ); + glColor3f(0, 1.0, 0); glVertex3f( 600, 410, 0 ); + glColor3f(0, 0, 1.0); glVertex3f( 600, 480, 0 ); + glColor3f(1.0, 1.0, 1.0); glVertex3f( 410, 470, 0 ); + glEnd(); + + SDL_GL_SwapBuffers(); + +#if !EMSCRIPTEN + // Wait for 3 seconds to give us a chance to see the image + SDL_Delay(3000); +#endif + + // Now we can delete the OpenGL texture and close down SDL + glDeleteTextures( 1, &texture ); + + SDL_Quit(); + + return 0; +} diff --git a/tests/test_browser.py b/tests/test_browser.py index 323677baeff9f..69fb6f7edf9e7 100644 --- a/tests/test_browser.py +++ b/tests/test_browser.py @@ -859,6 +859,11 @@ def test_sdl_ogl_p(self): args=['--preload-file', 'screenshot.png', '-s', 'LEGACY_GL_EMULATION=1'], message='You should see an image with gray at the top.') + def test_sdl_ogl_proc_alias(self): + shutil.copyfile(path_from_root('tests', 'screenshot.png'), os.path.join(self.get_dir(), 'screenshot.png')) + self.btest('sdl_ogl_proc_alias.c', reference='screenshot-gray-purple.png', reference_slack=1, + args=['-O2', '-g2', '-s', 'INLINING_LIMIT=1', '--preload-file', 'screenshot.png', '-s', 'LEGACY_GL_EMULATION=1', '-s', 'VERBOSE=1']) + def test_sdl_fog_simple(self): shutil.copyfile(path_from_root('tests', 'screenshot.png'), os.path.join(self.get_dir(), 'screenshot.png')) self.btest('sdl_fog_simple.c', reference='screenshot-fog-simple.png', From bff2b8740aba888bd67ee93198abc8fa0e116b80 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 16 Aug 2013 15:50:06 -0700 Subject: [PATCH 084/112] create FS.name_table at runtime, to reduce code size --- src/library_fs.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/library_fs.js b/src/library_fs.js index 51341a3e4d240..9d1f0cfda0bf2 100644 --- a/src/library_fs.js +++ b/src/library_fs.js @@ -18,7 +18,7 @@ mergeInto(LibraryManager.library, { devices: [null], streams: [null], nextInode: 1, - name_table: new Array(4096), + name_table: null, currentPath: '/', initialized: false, // Whether we are currently ignoring permissions. Useful when preparing the @@ -868,6 +868,8 @@ mergeInto(LibraryManager.library, { assert(stderr.fd === 3, 'invalid handle for stderr (' + stderr.fd + ')'); }, staticInit: function() { + FS.name_table = new Array(4096); + FS.root = FS.createNode(null, '/', {{{ cDefine('S_IFDIR') }}} | 0777, 0); FS.mount(MEMFS, {}, '/'); From b682e2a5d081c4479e50fc5a718b427963a7bc2f Mon Sep 17 00:00:00 2001 From: Anthony Pesch Date: Thu, 15 Aug 2013 10:21:21 -0700 Subject: [PATCH 085/112] capture charCode field of keypress events to backfill keydown events --- src/library_sdl.js | 44 +++++++++++++++++++++++++++++++++----------- 1 file changed, 33 insertions(+), 11 deletions(-) diff --git a/src/library_sdl.js b/src/library_sdl.js index 6cc337e8f22f3..024d28e0b55bd 100644 --- a/src/library_sdl.js +++ b/src/library_sdl.js @@ -379,7 +379,8 @@ var LibrarySDL = { SDL.surfaces[surf] = null; }, - touchX:0, touchY: 0, + touchX: 0, touchY: 0, + savedKeydown: null, receiveEvent: function(event) { switch(event.type) { @@ -466,11 +467,29 @@ var LibrarySDL = { SDL.DOMButtons[event.button] = 0; } - if (event.type == 'keypress' && !SDL.textInput) { - break; + // SDL expects a unicode character to be passed to its keydown events. + // Unfortunately, the browser APIs only provide a charCode property on + // keypress events, so we must backfill in keydown events with their + // subsequent keypress event's charCode. + if (event.type === 'keypress' && SDL.savedKeydown) { + // charCode is read-only + SDL.savedKeydown.keypressCharCode = event.charCode; + SDL.savedKeydown = null; + } else if (event.type === 'keydown') { + SDL.savedKeydown = event; + } + + // If we preventDefault on keydown events, the subsequent keypress events + // won't fire. However, it's fine (and in some cases necessary) to + // preventDefault for keys that don't generate a character. + if (event.type !== 'keydown' || (event.keyCode === 8 /* backspace */ || event.keyCode === 9 /* tab */)) { + event.preventDefault(); + } + + // Don't push keypress events unless SDL_StartTextInput has been called. + if (event.type !== 'keypress' || SDL.textInput) { + SDL.events.push(event); } - - SDL.events.push(event); break; case 'mouseout': // Un-press all pressed mouse buttons, because we might miss the release outside of the canvas @@ -485,6 +504,7 @@ var LibrarySDL = { SDL.DOMButtons[i] = 0; } } + event.preventDefault(); break; case 'blur': case 'visibilitychange': { @@ -495,6 +515,7 @@ var LibrarySDL = { keyCode: SDL.keyboardMap[code] }); } + event.preventDefault(); break; } case 'unload': @@ -506,16 +527,16 @@ var LibrarySDL = { return; case 'resize': SDL.events.push(event); + // manually triggered resize event doesn't have a preventDefault member + if (event.preventDefault) { + event.preventDefault(); + } break; } if (SDL.events.length >= 10000) { Module.printErr('SDL event queue full, dropping events'); SDL.events = SDL.events.slice(0, 10000); } - // manually triggered resize event doesn't have a preventDefault member - if (event.preventDefault) { - event.preventDefault(); - } return; }, @@ -590,8 +611,9 @@ var LibrarySDL = { {{{ makeSetValue('ptr', 'SDL.structs.KeyboardEvent.repeat', '0', 'i8') }}} // TODO {{{ makeSetValue('ptr', 'SDL.structs.KeyboardEvent.keysym + SDL.structs.keysym.scancode', 'scan', 'i32') }}} {{{ makeSetValue('ptr', 'SDL.structs.KeyboardEvent.keysym + SDL.structs.keysym.sym', 'key', 'i32') }}} - {{{ makeSetValue('ptr', 'SDL.structs.KeyboardEvent.keysym + SDL.structs.keysym.mod', 'SDL.modState', 'i32') }}} - {{{ makeSetValue('ptr', 'SDL.structs.KeyboardEvent.keysym + SDL.structs.keysym.unicode', 'key', 'i32') }}} + {{{ makeSetValue('ptr', 'SDL.structs.KeyboardEvent.keysym + SDL.structs.keysym.mod', 'SDL.modState', 'i16') }}} + // some non-character keys (e.g. backspace and tab) won't have keypressCharCode set, fill in with the keyCode. + {{{ makeSetValue('ptr', 'SDL.structs.KeyboardEvent.keysym + SDL.structs.keysym.unicode', 'event.keypressCharCode || key', 'i32') }}} break; } From a63686c1bbf93288248ee26a9beec819e319a3b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jukka=20Jyl=C3=A4nki?= Date: Sat, 17 Aug 2013 22:00:16 +0300 Subject: [PATCH 086/112] Fix source map generation on Windows. Fixes #1333. --- tests/runner.py | 12 +++++++++ tests/test_core.py | 8 +++--- tools/source-maps/sourcemapper.js | 43 ++++++++++++++++++++++++++----- 3 files changed, 53 insertions(+), 10 deletions(-) diff --git a/tests/runner.py b/tests/runner.py index bdbd2676b3b1b..318946e683ffb 100755 --- a/tests/runner.py +++ b/tests/runner.py @@ -275,6 +275,18 @@ def run_native(self, filename, args): print "Output: " + output[0] return output[0] + # Tests that the given two paths are identical, modulo path delimiters. E.g. "C:/foo" is equal to "C:\foo". + def assertPathsIdentical(self, path1, path2): + path1 = path1.replace('\\', '/') + path2 = path2.replace('\\', '/') + return self.assertIdentical(path1, path2) + + # Tests that the given two multiline text content are identical, modulo line ending differences (\r\n on Windows, \n on Unix). + def assertTextDataIdentical(self, text1, text2): + text1 = text1.replace('\r\n', '\n') + text2 = text2.replace('\r\n', '\n') + return self.assertIdentical(text1, text2) + def assertIdentical(self, values, y): if type(values) not in [list, tuple]: values = [values] for x in values: diff --git a/tests/test_core.py b/tests/test_core.py index 31db6ca52296d..88f6674a0af1c 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -9582,15 +9582,15 @@ def clean(code): self.assertIdentical(clean(no_maps_file), clean(out_file)) map_filename = out_filename + '.map' data = json.load(open(map_filename, 'r')) - self.assertIdentical(out_filename, data['file']) - self.assertIdentical(src_filename, data['sources'][0]) - self.assertIdentical(src, data['sourcesContent'][0]) + self.assertPathsIdentical(out_filename, data['file']) + self.assertPathsIdentical(src_filename, data['sources'][0]) + self.assertTextDataIdentical(src, data['sourcesContent'][0]) mappings = json.loads(jsrun.run_js( path_from_root('tools', 'source-maps', 'sourcemap2json.js'), tools.shared.NODE_JS, [map_filename])) seen_lines = set() for m in mappings: - self.assertIdentical(src_filename, m['source']) + self.assertPathsIdentical(src_filename, m['source']) seen_lines.add(m['originalLine']) # ensure that all the 'meaningful' lines in the original code get mapped assert seen_lines.issuperset([6, 7, 11, 12]) diff --git a/tools/source-maps/sourcemapper.js b/tools/source-maps/sourcemapper.js index fa908900352a2..06c9a2277ff11 100755 --- a/tools/source-maps/sourcemapper.js +++ b/tools/source-maps/sourcemapper.js @@ -16,6 +16,38 @@ function countLines(s) { return count; } +// For a minor optimization, only do win32->unix normalization if we are actually on Windows, +// which avoids redundantly scanning files if not needed. +var isWindows = (process.platform === 'win32'); + +var unixPathRe = new RegExp('\\\\', 'g'); +// Returns the given (possibly Windows) path p normalized to unix path separators '/'. +function toUnixPath(p) { + if (isWindows) { + return p.replace(unixPathRe, '/'); + } else { + return p; + } +} + +var unixLineEndRe = new RegExp('\r\n', 'g'); +// Returns the given (possibly Windows) text data t normalized to unix line endings '\n'. +function toUnixLineEnding(t) { + if (isWindows) { + return t.replace(unixLineEndRe, '\n'); + } else { + return t; + } +} + +// If path "p2" is a relative path, joins paths p1 and p2 to form "p1/p2". If p2 is an absolute path, "p2" is returned. +function joinPath(p1, p2) { + if (p2[0] == '/' || (p2.length >= 3 && p2[1] == ':' && (p2[2] == '/' || p2[2] == '\\'))) // Is p2 an absolute path? + return p2; + else + return toUnixPath(path.join(p1, p2)); +} + /* * Extracts the line (not block) comments from the generated function code and * invokes commentHandler with (comment content, line number of comment). This @@ -105,8 +137,7 @@ function generateMap(mappings, sourceRoot, mapFileBaseName, generatedLineOffset) // avoid doing it unnecessarily if (!(originalFileName in seenFiles)) { seenFiles[originalFileName] = true; - var rootedPath = originalFileName[0] === path.sep ? - originalFileName : path.join(sourceRoot, originalFileName); + var rootedPath = joinPath(sourceRoot, originalFileName); try { generator.setSourceContent(originalFileName, fs.readFileSync(rootedPath, 'utf-8')); } catch (e) { @@ -144,15 +175,15 @@ if (require.main === module) { } else { var opts = parseArgs(process.argv.slice(2)); var fileName = opts._[0]; - var sourceRoot = opts.sourceRoot ? opts.sourceRoot : "."; - var mapFileBaseName = opts.mapFileBaseName ? opts.mapFileBaseName : fileName; + var sourceRoot = opts.sourceRoot ? toUnixPath(opts.sourceRoot) : "."; + var mapFileBaseName = toUnixPath(opts.mapFileBaseName ? opts.mapFileBaseName : fileName); var generatedLineOffset = opts.offset ? parseInt(opts.offset, 10) : 0; - var generatedSource = fs.readFileSync(fileName, 'utf-8'); + var generatedSource = toUnixLineEnding(fs.readFileSync(fileName, 'utf-8')); var source = generatedSource; var mappings = getMappings(generatedSource); for (var i = 1, l = opts._.length; i < l; i ++) { - var optimizedSource = fs.readFileSync(opts._[i], 'utf-8') + var optimizedSource = toUnixLineEnding(fs.readFileSync(opts._[i], 'utf-8')) var optimizedMappings = getMappings(optimizedSource); var newMappings = {}; // uglify processes the code between EMSCRIPTEN_START_FUNCS and From bcb44e22e30d07dbbcaa4683e6b43f534227703c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jukka=20Jyl=C3=A4nki?= Date: Sat, 17 Aug 2013 23:04:44 +0300 Subject: [PATCH 087/112] Fix escaping of parameters in response file generation for Windows. Fixes #1471. Possibly related to the problem reported in #1533. --- tools/response_file.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/response_file.py b/tools/response_file.py index 312cda73bcab2..f19cf8af1a173 100644 --- a/tools/response_file.py +++ b/tools/response_file.py @@ -6,8 +6,8 @@ def create_response_file(args, directory): (response_fd, response_filename) = tempfile.mkstemp(prefix='emscripten_', suffix='.rsp', dir=directory, text=True) response_fd = os.fdopen(response_fd, "w") #print >> sys.stderr, "Creating response file '%s'" % response_filename - args = map(lambda p: p.replace(' ', '').replace('\\', '\\\\').replace('"', '\\"'), args) - response_fd.write(' '.join(args)) + args = map(lambda p: p.replace('\\', '\\\\').replace('"', '\\"'), args) + response_fd.write('"' + '" "'.join(args) + '"') response_fd.close() return response_filename From dcffa595b50ee767fc45e78ffb93cf9075e48c84 Mon Sep 17 00:00:00 2001 From: ToadKing Date: Sun, 18 Aug 2013 02:51:19 -0400 Subject: [PATCH 088/112] implement eglGetProcAddress --- src/library_egl.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/library_egl.js b/src/library_egl.js index 0e96e92f46fbc..ff912ed27027a 100644 --- a/src/library_egl.js +++ b/src/library_egl.js @@ -489,6 +489,11 @@ var LibraryEGL = { eglSwapBuffers: function() { EGL.setErrorCode(0x3000 /* EGL_SUCCESS */); }, + + eglGetProcAddress__deps: ['emscripten_GetProcAddress'], + eglGetProcAddress: function(name_) { + return _emscripten_GetProcAddress(Pointer_stringify(name_)); + }, }; autoAddDeps(LibraryEGL, '$EGL'); From 1cc24592558669e1816b7458eb48e7202f50deb9 Mon Sep 17 00:00:00 2001 From: Anthony Pesch Date: Sun, 18 Aug 2013 10:09:13 -0700 Subject: [PATCH 089/112] clean up sv sockets for socket tests --- tests/sockets/test_sockets_echo_server.c | 8 +++++++ tests/sockets/test_sockets_partial_server.c | 24 ++++++++++----------- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/tests/sockets/test_sockets_echo_server.c b/tests/sockets/test_sockets_echo_server.c index 8a48b8782174f..38e27cac5861b 100644 --- a/tests/sockets/test_sockets_echo_server.c +++ b/tests/sockets/test_sockets_echo_server.c @@ -37,6 +37,11 @@ typedef struct { server_t server; client_t client; +void cleanup() { + if (server.fd) close(server.fd); + if (client.fd) close(client.fd); +} + void main_loop(void *arg) { int res; fd_set fdr; @@ -105,6 +110,9 @@ int main() { struct sockaddr_in addr; int res; + atexit(cleanup); + signal(SIGTERM, cleanup); + memset(&server, 0, sizeof(server_t)); memset(&client, 0, sizeof(client_t)); diff --git a/tests/sockets/test_sockets_partial_server.c b/tests/sockets/test_sockets_partial_server.c index 57fae84b9d3f9..dfe0e249e1a11 100644 --- a/tests/sockets/test_sockets_partial_server.c +++ b/tests/sockets/test_sockets_partial_server.c @@ -14,18 +14,13 @@ #include #endif -int serverfd = -1; -int clientfd = -1; - -// csock.send("\x09\x01\x02\x03\x04\x05\x06\x07\x08\x09") -// csock.send("\x08\x01\x02\x03\x04\x05\x06\x07\x08") -// csock.send("\x07\x01\x02\x03\x04\x05\x06\x07") -// csock.send("\x06\x01\x02\x03\x04\x05\x06") -// csock.send("\x05\x01\x02\x03\x04\x05") -// csock.send("\x04\x01\x02\x03\x04") -// csock.send("\x03\x01\x02\x03") -// csock.send("\x02\x01\x02") -// csock.send("\x01\x01") +int serverfd = 0; +int clientfd = 0; + +void cleanup() { + if (serverfd) close(serverfd); + if (clientfd) close(clientfd); +} void do_send(int sockfd) { static char* buffers[] = { @@ -69,7 +64,7 @@ void iter(void *arg) { FD_ZERO(&fdr); FD_ZERO(&fdw); FD_SET(serverfd, &fdr); - if (clientfd != -1) FD_SET(clientfd, &fdw); + if (clientfd) FD_SET(clientfd, &fdw); res = select(64, &fdr, &fdw, NULL, NULL); if (res == -1) { perror("select failed"); @@ -91,6 +86,9 @@ int main() { struct sockaddr_in addr; int res; + atexit(cleanup); + signal(SIGTERM, cleanup); + // create the socket serverfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); if (serverfd == -1) { From c4bd45a8f3f3cc80f64cd78a74d014822c412c79 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Sun, 18 Aug 2013 18:03:24 -0700 Subject: [PATCH 090/112] restore EMSCRIPTEN_KEEPALIVE, needed to prevent inlining, EXPORTED_FUNCTIONS is too late for that --- system/include/emscripten/emscripten.h | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/system/include/emscripten/emscripten.h b/system/include/emscripten/emscripten.h index 28e6063cac1ae..f0df8dcaa22da 100644 --- a/system/include/emscripten/emscripten.h +++ b/system/include/emscripten/emscripten.h @@ -20,14 +20,13 @@ extern "C" { /* * Forces LLVM to not dead-code-eliminate a function. Note that - * closure may still eliminate it at the JS level, for which you - * should use EXPORTED_FUNCTIONS (see settings.js). - * - * **DEPRECATED**: Use EXPORTED_FUNCTIONS instead, which will work - * with closure, asm.js, etc. For example - * -s EXPORTED_FUNCTIONS=["_main", "myfunc"] + * you still need to use EXPORTED_FUNCTIONS so it stays alive + * in JS, e.g. + * emcc -s EXPORTED_FUNCTIONS=["_main", "_myfunc"] + * and in the source file + * void EMSCRIPTEN_KEEPALIVE myfunc() {..} */ -/* #define EMSCRIPTEN_KEEPALIVE __attribute__((used)) */ +#define EMSCRIPTEN_KEEPALIVE __attribute__((used)) /* * Interface to the underlying JS engine. This function will From f6416d822bd5178065272877505de4428b4aee2a Mon Sep 17 00:00:00 2001 From: Anthony Pesch Date: Thu, 15 Aug 2013 13:20:02 -0700 Subject: [PATCH 091/112] re-enabled non-asm.js shared lib support --- src/compiler.js | 3 +-- src/jsifier.js | 54 +++++++++++++++++++++++-------------------- src/settings.js | 3 ++- tests/test_browser.py | 2 +- tests/test_core.py | 7 +----- tests/test_other.py | 2 +- 6 files changed, 35 insertions(+), 36 deletions(-) diff --git a/src/compiler.js b/src/compiler.js index 365ff32fb8469..7595981bc7fb4 100644 --- a/src/compiler.js +++ b/src/compiler.js @@ -185,8 +185,7 @@ if (ASM_JS) { assert(!ALLOW_MEMORY_GROWTH, 'Cannot grow asm.js heap'); assert((TOTAL_MEMORY&(TOTAL_MEMORY-1)) == 0, 'asm.js heap must be power of 2'); } -assert(!BUILD_AS_SHARED_LIB, 'shared libs are deprecated'); -//assert(!(!NAMED_GLOBALS && BUILD_AS_SHARED_LIB), 'shared libraries must have named globals'); +assert(!(!NAMED_GLOBALS && BUILD_AS_SHARED_LIB), 'shared libraries must have named globals'); // Output some info and warnings based on settings diff --git a/src/jsifier.js b/src/jsifier.js index c92526d27f8a0..14afe96975f1b 100644 --- a/src/jsifier.js +++ b/src/jsifier.js @@ -278,7 +278,7 @@ function JSify(data, functionsOnly, givenFunctions) { // they would shadow similarly-named globals in the parent. item.JS = ''; } else { - item.JS = makeGlobalDef(item.ident); + item.JS = makeGlobalDef(item.ident); } if (!NAMED_GLOBALS && isIndexableGlobal(item.ident)) { @@ -1628,7 +1628,7 @@ function JSify(data, functionsOnly, givenFunctions) { // if (!mainPass) { - if (phase == 'pre' && !Variables.generatedGlobalBase) { + if (phase == 'pre' && !Variables.generatedGlobalBase && !BUILD_AS_SHARED_LIB) { Variables.generatedGlobalBase = true; // Globals are done, here is the rest of static memory assert((TARGET_LE32 && Runtime.GLOBAL_BASE == 8) || (TARGET_X86 && Runtime.GLOBAL_BASE == 4)); // this is assumed in e.g. relocations for linkable modules @@ -1672,24 +1672,26 @@ function JSify(data, functionsOnly, givenFunctions) { print('}\n'); if (USE_TYPED_ARRAYS == 2) { - print('var tempDoublePtr = Runtime.alignMemory(allocate(12, "i8", ALLOC_STATIC), 8);\n'); - print('assert(tempDoublePtr % 8 == 0);\n'); - print('function copyTempFloat(ptr) { // functions, because inlining this code increases code size too much\n'); - print(' HEAP8[tempDoublePtr] = HEAP8[ptr];\n'); - print(' HEAP8[tempDoublePtr+1] = HEAP8[ptr+1];\n'); - print(' HEAP8[tempDoublePtr+2] = HEAP8[ptr+2];\n'); - print(' HEAP8[tempDoublePtr+3] = HEAP8[ptr+3];\n'); - print('}\n'); - print('function copyTempDouble(ptr) {\n'); - print(' HEAP8[tempDoublePtr] = HEAP8[ptr];\n'); - print(' HEAP8[tempDoublePtr+1] = HEAP8[ptr+1];\n'); - print(' HEAP8[tempDoublePtr+2] = HEAP8[ptr+2];\n'); - print(' HEAP8[tempDoublePtr+3] = HEAP8[ptr+3];\n'); - print(' HEAP8[tempDoublePtr+4] = HEAP8[ptr+4];\n'); - print(' HEAP8[tempDoublePtr+5] = HEAP8[ptr+5];\n'); - print(' HEAP8[tempDoublePtr+6] = HEAP8[ptr+6];\n'); - print(' HEAP8[tempDoublePtr+7] = HEAP8[ptr+7];\n'); - print('}\n'); + if (!BUILD_AS_SHARED_LIB) { + print('var tempDoublePtr = Runtime.alignMemory(allocate(12, "i8", ALLOC_STATIC), 8);\n'); + print('assert(tempDoublePtr % 8 == 0);\n'); + print('function copyTempFloat(ptr) { // functions, because inlining this code increases code size too much\n'); + print(' HEAP8[tempDoublePtr] = HEAP8[ptr];\n'); + print(' HEAP8[tempDoublePtr+1] = HEAP8[ptr+1];\n'); + print(' HEAP8[tempDoublePtr+2] = HEAP8[ptr+2];\n'); + print(' HEAP8[tempDoublePtr+3] = HEAP8[ptr+3];\n'); + print('}\n'); + print('function copyTempDouble(ptr) {\n'); + print(' HEAP8[tempDoublePtr] = HEAP8[ptr];\n'); + print(' HEAP8[tempDoublePtr+1] = HEAP8[ptr+1];\n'); + print(' HEAP8[tempDoublePtr+2] = HEAP8[ptr+2];\n'); + print(' HEAP8[tempDoublePtr+3] = HEAP8[ptr+3];\n'); + print(' HEAP8[tempDoublePtr+4] = HEAP8[ptr+4];\n'); + print(' HEAP8[tempDoublePtr+5] = HEAP8[ptr+5];\n'); + print(' HEAP8[tempDoublePtr+6] = HEAP8[ptr+6];\n'); + print(' HEAP8[tempDoublePtr+7] = HEAP8[ptr+7];\n'); + print('}\n'); + } } } @@ -1724,11 +1726,13 @@ function JSify(data, functionsOnly, givenFunctions) { legalizedI64s = legalizedI64sDefault; - print('STACK_BASE = STACKTOP = Runtime.alignMemory(STATICTOP);\n'); - print('staticSealed = true; // seal the static portion of memory\n'); - print('STACK_MAX = STACK_BASE + ' + TOTAL_STACK + ';\n'); - print('DYNAMIC_BASE = DYNAMICTOP = Runtime.alignMemory(STACK_MAX);\n'); - print('assert(DYNAMIC_BASE < TOTAL_MEMORY); // Stack must fit in TOTAL_MEMORY; allocations from here on may enlarge TOTAL_MEMORY\n'); + if (!BUILD_AS_SHARED_LIB) { + print('STACK_BASE = STACKTOP = Runtime.alignMemory(STATICTOP);\n'); + print('staticSealed = true; // seal the static portion of memory\n'); + print('STACK_MAX = STACK_BASE + ' + TOTAL_STACK + ';\n'); + print('DYNAMIC_BASE = DYNAMICTOP = Runtime.alignMemory(STACK_MAX);\n'); + print('assert(DYNAMIC_BASE < TOTAL_MEMORY); // Stack must fit in TOTAL_MEMORY; allocations from here on may enlarge TOTAL_MEMORY\n'); + } if (asmLibraryFunctions.length > 0) { print('// ASM_LIBRARY FUNCTIONS'); diff --git a/src/settings.js b/src/settings.js index 8757f7b9bdf37..cb64bfd912995 100644 --- a/src/settings.js +++ b/src/settings.js @@ -315,11 +315,12 @@ var SIDE_MODULE = 0; // Corresponds to MAIN_MODULE var BUILD_AS_SHARED_LIB = 0; // Whether to build the code as a shared library // 0 here means this is not a shared lib: It is a main file. - // All shared library options (1 and 2) are currently deprecated XXX // 1 means this is a normal shared lib, load it with dlopen() // 2 means this is a shared lib that will be linked at runtime, // which means it will insert its functions into // the global namespace. See STATIC_LIBS_TO_LOAD. + // + // Value 2 is currently deprecated. var RUNTIME_LINKED_LIBS = []; // If this is a main file (BUILD_AS_SHARED_LIB == 0), then // we will link these at runtime. They must have been built with // BUILD_AS_SHARED_LIB == 2. diff --git a/tests/test_browser.py b/tests/test_browser.py index cd5668a301e27..2ccc6d84b950d 100644 --- a/tests/test_browser.py +++ b/tests/test_browser.py @@ -1339,7 +1339,7 @@ def test_perspective(self): self.btest('perspective.c', reference='perspective.png', args=['-s', 'LEGACY_GL_EMULATION=1']) def test_runtimelink(self): - return self.skip('shared libs are deprecated') + return self.skip('BUILD_AS_SHARED_LIB=2 is deprecated') main, supp = self.setup_runtimelink_test() open(self.in_dir('supp.cpp'), 'w').write(supp) diff --git a/tests/test_core.py b/tests/test_core.py index 31db6ca52296d..ccb0823e1309b 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -5451,7 +5451,7 @@ def test_nestedstructs(self): self.do_run(src, '*16,0,4,8,8,12|20,0,4,4,8,12,12,16|24,0,20,0,4,4,8,12,12,16*\n*0,0,0,1,2,64,68,69,72*\n*2*') def test_runtimelink(self): - return self.skip('shared libs are deprecated') + return self.skip('BUILD_AS_SHARED_LIB=2 is deprecated') if Building.LLVM_OPTS: return self.skip('LLVM opts will optimize printf into puts in the parent, and the child will still look for puts') if Settings.ASM_JS: return self.skip('asm does not support runtime linking') @@ -5470,7 +5470,6 @@ def test_runtimelink(self): self.do_run(main, 'supp: 54,2\nmain: 56\nsupp see: 543\nmain see: 76\nok.') def test_dlfcn_basic(self): - return self.skip('shared libs are deprecated') if Settings.ASM_JS: return self.skip('TODO: dlopen in asm') Settings.NAMED_GLOBALS = 1 @@ -5525,7 +5524,6 @@ def process(filename): post_build=add_pre_run_and_checks) def test_dlfcn_qsort(self): - return self.skip('shared libs are deprecated') if self.emcc_args is None: return self.skip('requires emcc') if Settings.ASM_JS: return self.skip('TODO: dlopen in asm') @@ -5622,7 +5620,6 @@ def process(filename): post_build=add_pre_run_and_checks) def test_dlfcn_data_and_fptr(self): - return self.skip('shared libs are deprecated') if Settings.ASM_JS: return self.skip('TODO: dlopen in asm') if Building.LLVM_OPTS: return self.skip('LLVM opts will optimize out parent_func') @@ -5727,7 +5724,6 @@ def process(filename): post_build=add_pre_run_and_checks) def test_dlfcn_alias(self): - return self.skip('shared libs are deprecated') if Settings.ASM_JS: return self.skip('TODO: dlopen in asm') Settings.LINKABLE = 1 @@ -5785,7 +5781,6 @@ def process(filename): Settings.INCLUDE_FULL_LIBRARY = 0 def test_dlfcn_varargs(self): - return self.skip('shared libs are deprecated') if Settings.ASM_JS: return self.skip('TODO: dlopen in asm') Settings.LINKABLE = 1 diff --git a/tests/test_other.py b/tests/test_other.py index b3e8a2a179a5e..3b3887d6aa247 100644 --- a/tests/test_other.py +++ b/tests/test_other.py @@ -1033,7 +1033,7 @@ def test_local_link(self): self.assertContained('hello from lib', run_js(os.path.join(self.get_dir(), 'a.out.js'))) def test_runtimelink_multi(self): - return self.skip('shared libs are deprecated') + return self.skip('BUILD_AS_SHARED_LIB=2 is deprecated') if Settings.ASM_JS: return self.skip('asm does not support runtime linking yet') if SPIDERMONKEY_ENGINE not in JS_ENGINES: return self.skip('cannot run without spidermonkey due to node limitations') From cc8297b311c8deaf6bf991796f93b3177ff94cd1 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Mon, 19 Aug 2013 10:32:21 -0700 Subject: [PATCH 092/112] emit preload timing --- src/postamble.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/postamble.js b/src/postamble.js index 08c3a9d8b4ebe..d6a452f843ea2 100644 --- a/src/postamble.js +++ b/src/postamble.js @@ -11,12 +11,18 @@ ExitStatus.prototype.constructor = ExitStatus; var initialStackTop; +var preloadStartTime = null; + Module['callMain'] = Module.callMain = function callMain(args) { assert(runDependencies == 0, 'cannot call main when async dependencies remain! (listen on __ATMAIN__)'); assert(__ATPRERUN__.length == 0, 'cannot call main when preRun functions remain to be called'); args = args || []; + if (preloadStartTime !== null) { + Module.printErr('preload time: ' + (Date.now() - preloadStartTime) + ' ms'); + } + ensureInitRuntime(); var argc = args.length+1; @@ -72,6 +78,8 @@ Module['callMain'] = Module.callMain = function callMain(args) { function run(args) { args = args || Module['arguments']; + if (preloadStartTime === null) preloadStartTime = Date.now(); + if (runDependencies > 0) { Module.printErr('run() called, but dependencies remain, so not running'); return; From 2468de960b3025174c5d2920b50e75fc87678250 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Mon, 19 Aug 2013 12:51:10 -0700 Subject: [PATCH 093/112] add 'other' to default test runner modes --- tests/runner.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/runner.py b/tests/runner.py index 318946e683ffb..ed68463ca9346 100755 --- a/tests/runner.py +++ b/tests/runner.py @@ -36,7 +36,7 @@ def path_from_root(*pathelems): # Core test runner class, shared between normal tests and benchmarks checked_sanity = False -test_modes = ['default', 'o1', 'o2', 'asm1', 'asm2', 'asm2g', 'asm2x86', 's_0_0', 's_0_1'] +test_modes = ['default', 'o1', 'o2', 'asm1', 'asm2', 'asm2g', 'asm2x86', 's_0_0', 's_0_1', 'other'] test_index = 0 class RunnerCore(unittest.TestCase): From cd38275faf739ba151c0aa7abe13703c9b8d8235 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Mon, 19 Aug 2013 13:39:51 -0700 Subject: [PATCH 094/112] show preload time only on web --- src/postamble.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/postamble.js b/src/postamble.js index d6a452f843ea2..df84412129f64 100644 --- a/src/postamble.js +++ b/src/postamble.js @@ -19,7 +19,7 @@ Module['callMain'] = Module.callMain = function callMain(args) { args = args || []; - if (preloadStartTime !== null) { + if (ENVIRONMENT_IS_WEB && preloadStartTime !== null) { Module.printErr('preload time: ' + (Date.now() - preloadStartTime) + ' ms'); } From a864b58025a0a41bf3eb00aa79d569ae9576c52f Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Mon, 19 Aug 2013 15:17:09 -0700 Subject: [PATCH 095/112] use -- for jsc --- tools/jsrun.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/jsrun.py b/tools/jsrun.py index 27c5535058cbd..571e9cee188e2 100644 --- a/tools/jsrun.py +++ b/tools/jsrun.py @@ -15,7 +15,7 @@ def timeout_run(proc, timeout, note='unnamed process', full_output=False): def run_js(filename, engine=None, args=[], check_timeout=False, stdout=PIPE, stderr=None, cwd=None, full_output=False): if type(engine) is not list: engine = [engine] - command = engine + [filename] + (['--'] if 'd8' in engine[0] else []) + args + command = engine + [filename] + (['--'] if 'd8' in engine[0] or 'jsc' in engine[0] else []) + args return timeout_run( Popen( command, From f6c69855c9e8711f0030d360b26af2b7ef94250f Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Mon, 19 Aug 2013 15:23:48 -0700 Subject: [PATCH 096/112] fix test suite ALL.* --- tests/runner.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/runner.py b/tests/runner.py index ed68463ca9346..bbbc23e5a10c0 100755 --- a/tests/runner.py +++ b/tests/runner.py @@ -36,7 +36,7 @@ def path_from_root(*pathelems): # Core test runner class, shared between normal tests and benchmarks checked_sanity = False -test_modes = ['default', 'o1', 'o2', 'asm1', 'asm2', 'asm2g', 'asm2x86', 's_0_0', 's_0_1', 'other'] +test_modes = ['default', 'o1', 'o2', 'asm1', 'asm2', 'asm2g', 'asm2x86', 's_0_0', 's_0_1'] test_index = 0 class RunnerCore(unittest.TestCase): @@ -732,6 +732,7 @@ def btest(self, filename, expected=None, reference=None, force_c=False, referenc ============================================================================== Running the main part of the test suite. Don't forget to run the other parts! + other - tests separate from the main suite sanity - tests for first run, etc., modifies ~/.emscripten benchmark - run before and after each set of changes before pushing to master, verify no regressions From c1e32c13f025f04a332eb46b2bd7930bba31933c Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Mon, 19 Aug 2013 15:27:20 -0700 Subject: [PATCH 097/112] disable use of SIGTERM in socket server, does not build in all linuxes --- tests/sockets/test_sockets_echo_server.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/sockets/test_sockets_echo_server.c b/tests/sockets/test_sockets_echo_server.c index 38e27cac5861b..f01004c3f1ef5 100644 --- a/tests/sockets/test_sockets_echo_server.c +++ b/tests/sockets/test_sockets_echo_server.c @@ -111,7 +111,7 @@ int main() { int res; atexit(cleanup); - signal(SIGTERM, cleanup); + //signal(SIGTERM, cleanup); memset(&server, 0, sizeof(server_t)); memset(&client, 0, sizeof(client_t)); From e4ef4ef3867565322877915d202d6a905f0f9867 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Mon, 19 Aug 2013 15:30:11 -0700 Subject: [PATCH 098/112] remove another SIGTERM --- tests/sockets/test_sockets_partial_server.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/sockets/test_sockets_partial_server.c b/tests/sockets/test_sockets_partial_server.c index dfe0e249e1a11..21096552c909c 100644 --- a/tests/sockets/test_sockets_partial_server.c +++ b/tests/sockets/test_sockets_partial_server.c @@ -87,7 +87,7 @@ int main() { int res; atexit(cleanup); - signal(SIGTERM, cleanup); + //signal(SIGTERM, cleanup); // create the socket serverfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); From 976aaf7c8aabc5a82aeed5485fb0fce7ad2e7409 Mon Sep 17 00:00:00 2001 From: James Gregory Date: Mon, 19 Aug 2013 16:40:37 -0700 Subject: [PATCH 099/112] Add test-case for touch-handling in glut. --- tests/glut_touchevents.c | 64 ++++++++++++++++++++++++++++++++++++++++ tests/test_browser.py | 3 ++ 2 files changed, 67 insertions(+) create mode 100644 tests/glut_touchevents.c diff --git a/tests/glut_touchevents.c b/tests/glut_touchevents.c new file mode 100644 index 0000000000000..1f09789586996 --- /dev/null +++ b/tests/glut_touchevents.c @@ -0,0 +1,64 @@ +#include +#include +#include +#include +#include + +#define MULTILINE(...) #__VA_ARGS__ + +int touch_started = 0; +int touch_ended = 0; + +int result = 0; + +void mouseCB(int button, int state, int x, int y) +{ + if(button == GLUT_LEFT_BUTTON) + { + if(state == GLUT_DOWN) + { + touch_started = 1; + } + else if(state == GLUT_UP) + { + touch_ended = 1; + } + } +} + +int main(int argc, char *argv[]) +{ + emscripten_run_script(MULTILINE( + Module.injectEvent = function(eventType, x, y) { + // Desktop browsers do not have the event types for touch events, + // so we fake them by creating a plain-vanilla UIEvent and then + // filling in the fields that we look for with appropriate values. + var touch = { + pageX: x, + pageY: y + }; + var touches = [ touch ]; + touches.item = function(i) { return this[i]; }; + + var event = document.createEvent('UIEvent'); + event.target = Module['canvas']; + event.button = 0; + event.touches = touches; + event.initUIEvent(eventType, true, true); + Module['canvas'].dispatchEvent(event); + } + )); + + // Fake a touch device so that glut sets up the appropriate event handlers. + emscripten_run_script("document.documentElement['ontouchstart'] = 1"); + glutInit(&argc, argv); + + glutMouseFunc(&mouseCB); + + emscripten_run_script("Module.injectEvent('touchend', 100, 100)"); + emscripten_run_script("Module.injectEvent('touchstart', 100, 100)"); + result = touch_started && touch_ended; + + REPORT_RESULT(); + return 0; +} diff --git a/tests/test_browser.py b/tests/test_browser.py index 2c5d65eea3e10..7c387071ff4a0 100644 --- a/tests/test_browser.py +++ b/tests/test_browser.py @@ -787,6 +787,9 @@ def test_sdl_mouse_offsets(self): Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'sdl_mouse.c'), '-O2', '--minify', '0', '-o', 'sdl_mouse.js', '--pre-js', 'pre.js']).communicate() self.run_browser('page.html', '', '/report_result?600') + def test_glut_touchevents(self): + self.btest('glut_touchevents.c', '1') + def test_sdl_pumpevents(self): # key events should be detected using SDL_PumpEvents open(os.path.join(self.get_dir(), 'pre.js'), 'w').write(''' From 83972554f5fce6b2205d28211e115195dbdf6523 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Mon, 19 Aug 2013 17:33:46 -0700 Subject: [PATCH 100/112] round doubles to i64s more carefully, especially small negatives; fixes #1539 --- src/parseTools.js | 28 +++++++++++++++-- tests/test_core.py | 77 +++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 101 insertions(+), 4 deletions(-) diff --git a/src/parseTools.js b/src/parseTools.js index 65e962648ebc5..046dac1b9d8f6 100644 --- a/src/parseTools.js +++ b/src/parseTools.js @@ -727,15 +727,37 @@ function makeI64(low, high) { // Splits a number (an integer in a double, possibly > 32 bits) into an USE_TYPED_ARRAYS == 2 i64 value. // Will suffer from rounding. mergeI64 does the opposite. function splitI64(value, floatConversion) { - // We need to min here, since our input might be a double, and large values are rounded, so they can + // general idea: + // + // $1$0 = ~~$d >>> 0; + // $1$1 = Math_abs($d) >= 1 ? ( + // $d > 0 ? Math.min(Math_floor(($d)/ 4294967296.0), 4294967295.0) + // : Math_ceil(Math.min(-4294967296.0, $d - $1$0)/ 4294967296.0) + // ) : 0; + // + // We need to min on positive values here, since our input might be a double, and large values are rounded, so they can // be slightly higher than expected. And if we get 4294967296, that will turn into a 0 if put into a // HEAP32 or |0'd, etc. + // + // For negatives, we need to ensure a -1 if the value is overall negative, even if not significant negative component + var lowInput = legalizedI64s ? value : 'VALUE'; if (floatConversion && ASM_JS) lowInput = asmFloatToInt(lowInput); + var low = lowInput + '>>>0'; + var high = makeInlineCalculation( + asmCoercion('Math.abs(VALUE)', 'double') + ' >= ' + asmEnsureFloat('1', 'double') + ' ? ' + + '(VALUE > ' + asmEnsureFloat('0', 'double') + ' ? ' + + asmCoercion('Math.min(' + asmCoercion('Math.floor((VALUE)/' + asmEnsureFloat(4294967296, 'float') + ')', 'double') + ', ' + asmEnsureFloat(4294967295, 'float') + ')', 'i32') + '>>>0' + + ' : ' + asmFloatToInt(asmCoercion('Math.ceil((VALUE - +((' + asmFloatToInt('VALUE') + ')>>>0))/' + asmEnsureFloat(4294967296, 'float') + ')', 'double')) + '>>>0' + + ')' + + ' : 0', + value, + 'tempDouble' + ); if (legalizedI64s) { - return [lowInput + '>>>0', asmCoercion('Math.min(' + asmCoercion('Math.floor((' + value + ')/' + asmEnsureFloat(4294967296, 'float') + ')', 'double') + ', ' + asmEnsureFloat(4294967295, 'float') + ')', 'i32') + '>>>0']; + return [low, high]; } else { - return makeInlineCalculation(makeI64(lowInput + '>>>0', asmCoercion('Math.min(' + asmCoercion('Math.floor(VALUE/' + asmEnsureFloat(4294967296, 'float') + ')', 'double') + ', ' + asmEnsureFloat(4294967295, 'float') + ')', 'i32') + '>>>0'), value, 'tempBigIntP'); + return makeI64(low, high); } } function mergeI64(value, unsigned) { diff --git a/tests/test_core.py b/tests/test_core.py index 35f5f0f9c8107..f3ddc4f446aa5 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -808,6 +808,80 @@ def test_i16_emcc_intrinsic(self): ''' self.do_run(src, ',0,,2,C!,0,C!,0,,65535,C!,0,') + def test_double_i64_conversion(self): + if Settings.USE_TYPED_ARRAYS != 2: return self.skip('needs ta2') + + src = r''' + #include + #include + #include + + __attribute((noinline)) bool eq(double d, int64_t i) { + int64_t i2 = (int64_t)d; + if (i != i2) { + printf("%.20g converted to int64 returns %lld, not %lld as expected!\n", d, i2, i); + } + return i == i2; + } + + int main() { + assert(eq(0.0, 0)); + assert(eq(-0.0, 0)); + assert(eq(0.1, 0)); + assert(eq(-0.1, 0)); + assert(eq(0.6, 0)); + assert(eq(-0.6, 0)); + assert(eq(1.0, 1)); + assert(eq(-1.0, -1)); + assert(eq(1.1, 1)); + assert(eq(-1.1, -1)); + assert(eq(1.6, 1)); + assert(eq(-1.6, -1)); + assert(eq(4294967295.0, 4294967295LL)); + assert(eq(4294967295.5, 4294967295LL)); + assert(eq(4294967296.0, 4294967296LL)); + assert(eq(4294967296.5, 4294967296LL)); + assert(eq(14294967295.0, 14294967295LL)); + assert(eq(14294967295.5, 14294967295LL)); + assert(eq(14294967296.0, 14294967296LL)); + assert(eq(14294967296.5, 14294967296LL)); + assert(eq(-4294967295.0, -4294967295LL)); + assert(eq(-4294967295.5, -4294967295LL)); + assert(eq(-4294967296.0, -4294967296LL)); + assert(eq(-4294967296.5, -4294967296LL)); + assert(eq(-14294967295.0, -14294967295LL)); + assert(eq(-14294967295.5, -14294967295LL)); + assert(eq(-14294967296.0, -14294967296LL)); + assert(eq(-14294967296.5, -14294967296LL)); + + assert(eq(4294967295.3, 4294967295LL)); + assert(eq(4294967296.3, 4294967296LL)); + assert(eq(14294967295.3, 14294967295LL)); + assert(eq(14294967296.3, 14294967296LL)); + assert(eq(-4294967295.3, -4294967295LL)); + assert(eq(-4294967296.3, -4294967296LL)); + assert(eq(-14294967295.3, -14294967295LL)); + assert(eq(-14294967296.3, -14294967296LL)); + + assert(eq(4294967295.8, 4294967295LL)); + assert(eq(4294967296.8, 4294967296LL)); + assert(eq(14294967295.8, 14294967295LL)); + assert(eq(14294967296.8, 14294967296LL)); + assert(eq(-4294967295.8, -4294967295LL)); + assert(eq(-4294967296.8, -4294967296LL)); + assert(eq(-14294967295.8, -14294967295LL)); + assert(eq(-14294967296.8, -14294967296LL)); + + // The following number is the largest double such that all integers smaller than this can exactly be represented in a double. + assert(eq(9007199254740992.0, 9007199254740992LL /* == 2^53 */)); + assert(eq(-9007199254740992.0, -9007199254740992LL /* == -2^53 */)); + + printf("OK!\n"); + return 0; + } + ''' + self.do_run(src, 'OK!\n'); + def test_negative_zero(self): src = r''' #include @@ -10066,4 +10140,5 @@ def setUp(self): ) locals()[fullname] = make_run(fullname, fullname, compiler, embetter, quantum, typed_arrays) -del T # T is just a shape for the specific subclasses, we don't test it itself \ No newline at end of file +del T # T is just a shape for the specific subclasses, we don't test it itself + From bedb3bfacba7aa761777d1ede43d014b7e2c5ee4 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 20 Aug 2013 10:23:28 -0700 Subject: [PATCH 101/112] improve headless.js --- src/headless.js | 2 ++ src/jsifier.js | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/headless.js b/src/headless.js index 097a42f753bd6..1c7b8945a5884 100644 --- a/src/headless.js +++ b/src/headless.js @@ -23,6 +23,7 @@ var window = { return '%s'; }, search: '?%s', + pathname: '%s', }, fakeNow: 0, // we don't use Date.now() rafs: [], @@ -84,6 +85,7 @@ var window = { }, revokeObjectURL: function(x) {}, }, + encodeURIComponent: function(x) { return x }, }; var setTimeout = window.setTimeout; var document = { diff --git a/src/jsifier.js b/src/jsifier.js index 8884e24f44d74..c7742288c7093 100644 --- a/src/jsifier.js +++ b/src/jsifier.js @@ -1806,7 +1806,7 @@ function JSify(data, functionsOnly, givenFunctions) { } if (HEADLESS) { print('if (!ENVIRONMENT_IS_WEB) {'); - print(read('headless.js').replace("'%s'", "'http://emscripten.org'").replace("'?%s'", "''").replace('%s,', 'null,').replace('%d', '0')); + print(read('headless.js').replace("'%s'", "'http://emscripten.org'").replace("'?%s'", "''").replace("'?%s'", "'/'").replace('%s,', 'null,').replace('%d', '0')); print('}'); } if (RUNTIME_TYPE_INFO) { From d0dc717bfa79ba18a132363437662665775c7a36 Mon Sep 17 00:00:00 2001 From: Aidan Hobson Sayers Date: Sun, 18 Aug 2013 02:33:15 +0100 Subject: [PATCH 102/112] Add self to authors --- AUTHORS | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS b/AUTHORS index 5161f7adbc22c..6afe0918d8eea 100644 --- a/AUTHORS +++ b/AUTHORS @@ -93,4 +93,5 @@ a license to everyone to use it as detailed in LICENSE.) * Yu Kobayashi * Pin Zhang * Nick Bray (copyright owned by Google, Inc.) +* Aidan Hobson Sayers From e90b624b158bef01ea56999613d59662684edd2c Mon Sep 17 00:00:00 2001 From: Aidan Hobson Sayers Date: Sun, 18 Aug 2013 02:33:35 +0100 Subject: [PATCH 103/112] Fix 4 byte utf8 characters --- src/runtime.js | 38 +++++++++++++++++++++++++++----------- 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/src/runtime.js b/src/runtime.js index e07d5054ff8b8..959cdfd59b0c6 100644 --- a/src/runtime.js +++ b/src/runtime.js @@ -386,35 +386,51 @@ var Runtime = { // Returns a processor of UTF. // processCChar() receives characters from a C-like UTF representation and returns JS string fragments. + // See RFC3629 for details, the bytes are assumed to be valid UTF-8 // processJSString() receives a JS string and returns a C-like UTF representation in an array UTF8Processor: function() { var buffer = []; var needed = 0; this.processCChar = function (code) { - code = code & 0xff; - if (needed) { - buffer.push(code); - needed--; - } + code = code & 0xFF; + if (buffer.length == 0) { - if (code < 128) return String.fromCharCode(code); + if ((code & 0x80) == 0) { // 0xxxxxxx + return String.fromCharCode(code); + } buffer.push(code); - if (code > 191 && code < 224) { + if (((code & 0xE0) ^ 0xC0) == 0) { // 110xxxxx needed = 1; - } else { + } else if (((code & 0xF0) ^ 0xE0) == 0) { // 1110xxxx needed = 2; + } else { // 11110xxx + needed = 3; } return ''; } + + if (needed) { + buffer.push(code); + needed--; + } + if (needed > 0) return ''; var c1 = buffer[0]; var c2 = buffer[1]; var c3 = buffer[2]; + var c4 = buffer[3]; var ret; - if (c1 > 191 && c1 < 224) { - ret = String.fromCharCode(((c1 & 31) << 6) | (c2 & 63)); + if (buffer.length == 2) { + ret = String.fromCharCode(((c1 & 0x1F) << 6) | (c2 & 0x3F)); + } else if (buffer.length == 3) { + ret = String.fromCharCode(((c1 & 0x0F) << 12) | ((c2 & 0x3F) << 6) | (c3 & 0x3F)); } else { - ret = String.fromCharCode(((c1 & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63)); + // http://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae + var codePoint = ((c1 & 0x07) << 18) | ((c2 & 0x3F) << 12) | + ((c3 & 0x3F) << 6) | (c4 & 0x3F); + ret = String.fromCharCode( + Math.floor((codePoint - 0x10000) / 0x400) + 0xD800, + (codePoint - 0x10000) % 0x400 + 0xDC00); } buffer.length = 0; return ret; From 12a4321cf394f968c0553e770e18c0b621542a31 Mon Sep 17 00:00:00 2001 From: Aidan Hobson Sayers Date: Sun, 18 Aug 2013 02:34:24 +0100 Subject: [PATCH 104/112] Add test for fix --- tests/test_core.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/test_core.py b/tests/test_core.py index f3ddc4f446aa5..29a04e1eb7f9b 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -7105,14 +7105,15 @@ def test_utf(self): #include int main() { - char *c = "μ†ℱ ╋ℯ╳╋"; + char *c = "μ†ℱ ╋ℯ╳╋ 😇"; printf("%d %d %d %d %s\n", c[0]&0xff, c[1]&0xff, c[2]&0xff, c[3]&0xff, c); - emscripten_run_script("cheez = _malloc(100);" - "Module.writeStringToMemory(\"μ†ℱ ╋ℯ╳╋\", cheez);" - "Module.print([Pointer_stringify(cheez), Module.getValue(cheez, 'i8')&0xff, Module.getValue(cheez+1, 'i8')&0xff, Module.getValue(cheez+2, 'i8')&0xff, Module.getValue(cheez+3, 'i8')&0xff, ]);"); + emscripten_run_script( + "cheez = _malloc(100);" + "Module.writeStringToMemory(\"μ†ℱ ╋ℯ╳╋ 😇\", cheez);" + "Module.print([Pointer_stringify(cheez), Module.getValue(cheez, 'i8')&0xff, Module.getValue(cheez+1, 'i8')&0xff, Module.getValue(cheez+2, 'i8')&0xff, Module.getValue(cheez+3, 'i8')&0xff, ]);"); } ''' - self.do_run(src, '206 188 226 128 μ†ℱ ╋ℯ╳╋\nμ†ℱ ╋ℯ╳╋,206,188,226,128\n'); + self.do_run(src, '206 188 226 128 μ†ℱ ╋ℯ╳╋ 😇\nμ†ℱ ╋ℯ╳╋ 😇,206,188,226,128\n'); def test_direct_string_constant_usage(self): if self.emcc_args is None: return self.skip('requires libcxx') @@ -10141,4 +10142,3 @@ def setUp(self): locals()[fullname] = make_run(fullname, fullname, compiler, embetter, quantum, typed_arrays) del T # T is just a shape for the specific subclasses, we don't test it itself - From 09c1168629557ab46818785004bcb95b696e254c Mon Sep 17 00:00:00 2001 From: Aidan Hobson Sayers Date: Tue, 20 Aug 2013 18:00:53 +0100 Subject: [PATCH 105/112] Clearer (and faster?), as per jij's suggestion --- src/runtime.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/runtime.js b/src/runtime.js index 959cdfd59b0c6..868aacb6efe11 100644 --- a/src/runtime.js +++ b/src/runtime.js @@ -395,15 +395,15 @@ var Runtime = { code = code & 0xFF; if (buffer.length == 0) { - if ((code & 0x80) == 0) { // 0xxxxxxx + if ((code & 0x80) == 0x00) { // 0xxxxxxx return String.fromCharCode(code); } buffer.push(code); - if (((code & 0xE0) ^ 0xC0) == 0) { // 110xxxxx + if ((code & 0xE0) == 0xC0) { // 110xxxxx needed = 1; - } else if (((code & 0xF0) ^ 0xE0) == 0) { // 1110xxxx + } else if ((code & 0xF0) == 0xE0) { // 1110xxxx needed = 2; - } else { // 11110xxx + } else { // 11110xxx needed = 3; } return ''; From 7bf034909f6d117320680200d14d0937cabdd787 Mon Sep 17 00:00:00 2001 From: Aidan Hobson Sayers Date: Tue, 20 Aug 2013 20:19:17 +0100 Subject: [PATCH 106/112] Only check needed > 0 if it was formerly > 0 --- src/runtime.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/runtime.js b/src/runtime.js index 868aacb6efe11..33088ad9dab0c 100644 --- a/src/runtime.js +++ b/src/runtime.js @@ -412,9 +412,9 @@ var Runtime = { if (needed) { buffer.push(code); needed--; + if (needed > 0) return ''; } - if (needed > 0) return ''; var c1 = buffer[0]; var c2 = buffer[1]; var c3 = buffer[2]; From 1a1f30803f01bc2b1defcf7ec9ae038e995be884 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 20 Aug 2013 13:29:03 -0700 Subject: [PATCH 107/112] sync headless code from bb --- src/headless.js | 703 +++--------------------------------------- src/headlessCanvas.js | 618 +++++++++++++++++++++++++++++++++++++ src/jsifier.js | 2 + 3 files changed, 666 insertions(+), 657 deletions(-) create mode 100644 src/headlessCanvas.js diff --git a/src/headless.js b/src/headless.js index 1c7b8945a5884..e545864118ac8 100644 --- a/src/headless.js +++ b/src/headless.js @@ -1,23 +1,12 @@ //== HEADLESS ==// -// TODO: sync from bananabread headless.js +var headlessPrint = function(x) { + //print(x); +} var window = { - eventListeners: {}, - addEventListener: function(id, func) { - var listeners = this.eventListeners[id]; - if (!listeners) { - listeners = this.eventListeners[id] = []; - } - listeners.push(func); - }, - callEventListeners: function(id) { - var listeners = this.eventListeners[id]; - if (listeners) { - listeners.forEach(function(listener) { listener() }); - } - }, + // adjustable parameters location: { toString: function() { return '%s'; @@ -25,31 +14,37 @@ var window = { search: '?%s', pathname: '%s', }, + onIdle: function(){ headlessPrint('triggering click'); document.querySelector('.fullscreen-button.low-res').callEventListeners('click'); window.onIdle = null; }, + dirsToDrop: 0, // go back to root dir if first_js is in a subdir + // + + headless: true, + + stopped: false, fakeNow: 0, // we don't use Date.now() rafs: [], timeouts: [], uid: 0, requestAnimationFrame: function(func) { func.uid = window.uid++; - print('adding raf ' + func.uid); + headlessPrint('adding raf ' + func.uid); window.rafs.push(func); }, setTimeout: function(func, ms) { func.uid = window.uid++; - print('adding timeout ' + func.uid); + headlessPrint('adding timeout ' + func.uid); window.timeouts.push({ func: func, when: window.fakeNow + (ms || 0) }); window.timeouts.sort(function(x, y) { return y.when - x.when }); }, - onIdle: %s, runEventLoop: function() { // run forever until an exception stops this replay var iter = 0; - while (1) { - var start = Recorder.dnow(); - print('event loop: ' + (iter++)); + while (!this.stopped) { + var start = Date.realNow(); + headlessPrint('event loop: ' + (iter++)); if (window.rafs.length == 0 && window.timeouts.length == 0) { if (window.onIdle) { window.onIdle(); @@ -62,7 +57,7 @@ var window = { window.rafs = []; for (var i = 0; i < currRafs.length; i++) { var raf = currRafs[i]; - print('calling raf: ' + raf.uid);// + ': ' + raf.toString().substring(0, 50)); + headlessPrint('calling raf: ' + raf.uid);// + ': ' + raf.toString().substring(0, 50)); raf(); } // timeouts @@ -71,24 +66,14 @@ var window = { window.timeouts = []; while (timeouts.length && timeouts[timeouts.length-1].when <= now) { var timeout = timeouts.pop(); - print('calling timeout: ' + timeout.func.uid);// + ': ' + timeout.func.toString().substring(0, 50)); + headlessPrint('calling timeout: ' + timeout.func.uid);// + ': ' + timeout.func.toString().substring(0, 50)); timeout.func(); } // increment 'time' window.fakeNow += 16.666; - print('main event loop iteration took ' + (Recorder.dnow() - start) + ' ms'); + headlessPrint('main event loop iteration took ' + (Date.realNow() - start) + ' ms'); } }, - URL: { - createObjectURL: function(x) { - return x; // the blob itself is returned - }, - revokeObjectURL: function(x) {}, - }, - encodeURIComponent: function(x) { return x }, -}; -var setTimeout = window.setTimeout; -var document = { eventListeners: {}, addEventListener: function(id, func) { var listeners = this.eventListeners[id]; @@ -103,619 +88,25 @@ var document = { listeners.forEach(function(listener) { listener() }); } }, + URL: { + createObjectURL: function(x) { + return x; // the blob itself is returned + }, + revokeObjectURL: function(x) {}, + }, + encodeURIComponent: function(x) { return x }, +}; +var setTimeout = window.setTimeout; +var document = { + headless: true, + eventListeners: {}, + addEventListener: window.addEventListener, + callEventListeners: window.callEventListeners, getElementById: function(id) { switch(id) { case 'canvas': { if (this.canvas) return this.canvas; - return this.canvas = { - getContext: function(which) { - switch(which) { - case 'experimental-webgl': { - return { - /* ClearBufferMask */ - DEPTH_BUFFER_BIT : 0x00000100, - STENCIL_BUFFER_BIT : 0x00000400, - COLOR_BUFFER_BIT : 0x00004000, - - /* BeginMode */ - POINTS : 0x0000, - LINES : 0x0001, - LINE_LOOP : 0x0002, - LINE_STRIP : 0x0003, - TRIANGLES : 0x0004, - TRIANGLE_STRIP : 0x0005, - TRIANGLE_FAN : 0x0006, - - /* AlphaFunction (not supported in ES20) */ - /* NEVER */ - /* LESS */ - /* EQUAL */ - /* LEQUAL */ - /* GREATER */ - /* NOTEQUAL */ - /* GEQUAL */ - /* ALWAYS */ - - /* BlendingFactorDest */ - ZERO : 0, - ONE : 1, - SRC_COLOR : 0x0300, - ONE_MINUS_SRC_COLOR : 0x0301, - SRC_ALPHA : 0x0302, - ONE_MINUS_SRC_ALPHA : 0x0303, - DST_ALPHA : 0x0304, - ONE_MINUS_DST_ALPHA : 0x0305, - - /* BlendingFactorSrc */ - /* ZERO */ - /* ONE */ - DST_COLOR : 0x0306, - ONE_MINUS_DST_COLOR : 0x0307, - SRC_ALPHA_SATURATE : 0x0308, - /* SRC_ALPHA */ - /* ONE_MINUS_SRC_ALPHA */ - /* DST_ALPHA */ - /* ONE_MINUS_DST_ALPHA */ - - /* BlendEquationSeparate */ - FUNC_ADD : 0x8006, - BLEND_EQUATION : 0x8009, - BLEND_EQUATION_RGB : 0x8009, /* same as BLEND_EQUATION */ - BLEND_EQUATION_ALPHA : 0x883D, - - /* BlendSubtract */ - FUNC_SUBTRACT : 0x800A, - FUNC_REVERSE_SUBTRACT : 0x800B, - - /* Separate Blend Functions */ - BLEND_DST_RGB : 0x80C8, - BLEND_SRC_RGB : 0x80C9, - BLEND_DST_ALPHA : 0x80CA, - BLEND_SRC_ALPHA : 0x80CB, - CONSTANT_COLOR : 0x8001, - ONE_MINUS_CONSTANT_COLOR : 0x8002, - CONSTANT_ALPHA : 0x8003, - ONE_MINUS_CONSTANT_ALPHA : 0x8004, - BLEND_COLOR : 0x8005, - - /* Buffer Objects */ - ARRAY_BUFFER : 0x8892, - ELEMENT_ARRAY_BUFFER : 0x8893, - ARRAY_BUFFER_BINDING : 0x8894, - ELEMENT_ARRAY_BUFFER_BINDING : 0x8895, - - STREAM_DRAW : 0x88E0, - STATIC_DRAW : 0x88E4, - DYNAMIC_DRAW : 0x88E8, - - BUFFER_SIZE : 0x8764, - BUFFER_USAGE : 0x8765, - - CURRENT_VERTEX_ATTRIB : 0x8626, - - /* CullFaceMode */ - FRONT : 0x0404, - BACK : 0x0405, - FRONT_AND_BACK : 0x0408, - - /* DepthFunction */ - /* NEVER */ - /* LESS */ - /* EQUAL */ - /* LEQUAL */ - /* GREATER */ - /* NOTEQUAL */ - /* GEQUAL */ - /* ALWAYS */ - - /* EnableCap */ - /* TEXTURE_2D */ - CULL_FACE : 0x0B44, - BLEND : 0x0BE2, - DITHER : 0x0BD0, - STENCIL_TEST : 0x0B90, - DEPTH_TEST : 0x0B71, - SCISSOR_TEST : 0x0C11, - POLYGON_OFFSET_FILL : 0x8037, - SAMPLE_ALPHA_TO_COVERAGE : 0x809E, - SAMPLE_COVERAGE : 0x80A0, - - /* ErrorCode */ - NO_ERROR : 0, - INVALID_ENUM : 0x0500, - INVALID_VALUE : 0x0501, - INVALID_OPERATION : 0x0502, - OUT_OF_MEMORY : 0x0505, - - /* FrontFaceDirection */ - CW : 0x0900, - CCW : 0x0901, - - /* GetPName */ - LINE_WIDTH : 0x0B21, - ALIASED_POINT_SIZE_RANGE : 0x846D, - ALIASED_LINE_WIDTH_RANGE : 0x846E, - CULL_FACE_MODE : 0x0B45, - FRONT_FACE : 0x0B46, - DEPTH_RANGE : 0x0B70, - DEPTH_WRITEMASK : 0x0B72, - DEPTH_CLEAR_VALUE : 0x0B73, - DEPTH_FUNC : 0x0B74, - STENCIL_CLEAR_VALUE : 0x0B91, - STENCIL_FUNC : 0x0B92, - STENCIL_FAIL : 0x0B94, - STENCIL_PASS_DEPTH_FAIL : 0x0B95, - STENCIL_PASS_DEPTH_PASS : 0x0B96, - STENCIL_REF : 0x0B97, - STENCIL_VALUE_MASK : 0x0B93, - STENCIL_WRITEMASK : 0x0B98, - STENCIL_BACK_FUNC : 0x8800, - STENCIL_BACK_FAIL : 0x8801, - STENCIL_BACK_PASS_DEPTH_FAIL : 0x8802, - STENCIL_BACK_PASS_DEPTH_PASS : 0x8803, - STENCIL_BACK_REF : 0x8CA3, - STENCIL_BACK_VALUE_MASK : 0x8CA4, - STENCIL_BACK_WRITEMASK : 0x8CA5, - VIEWPORT : 0x0BA2, - SCISSOR_BOX : 0x0C10, - /* SCISSOR_TEST */ - COLOR_CLEAR_VALUE : 0x0C22, - COLOR_WRITEMASK : 0x0C23, - UNPACK_ALIGNMENT : 0x0CF5, - PACK_ALIGNMENT : 0x0D05, - MAX_TEXTURE_SIZE : 0x0D33, - MAX_VIEWPORT_DIMS : 0x0D3A, - SUBPIXEL_BITS : 0x0D50, - RED_BITS : 0x0D52, - GREEN_BITS : 0x0D53, - BLUE_BITS : 0x0D54, - ALPHA_BITS : 0x0D55, - DEPTH_BITS : 0x0D56, - STENCIL_BITS : 0x0D57, - POLYGON_OFFSET_UNITS : 0x2A00, - /* POLYGON_OFFSET_FILL */ - POLYGON_OFFSET_FACTOR : 0x8038, - TEXTURE_BINDING_2D : 0x8069, - SAMPLE_BUFFERS : 0x80A8, - SAMPLES : 0x80A9, - SAMPLE_COVERAGE_VALUE : 0x80AA, - SAMPLE_COVERAGE_INVERT : 0x80AB, - - /* GetTextureParameter */ - /* TEXTURE_MAG_FILTER */ - /* TEXTURE_MIN_FILTER */ - /* TEXTURE_WRAP_S */ - /* TEXTURE_WRAP_T */ - - COMPRESSED_TEXTURE_FORMATS : 0x86A3, - - /* HintMode */ - DONT_CARE : 0x1100, - FASTEST : 0x1101, - NICEST : 0x1102, - - /* HintTarget */ - GENERATE_MIPMAP_HINT : 0x8192, - - /* DataType */ - BYTE : 0x1400, - UNSIGNED_BYTE : 0x1401, - SHORT : 0x1402, - UNSIGNED_SHORT : 0x1403, - INT : 0x1404, - UNSIGNED_INT : 0x1405, - FLOAT : 0x1406, - - /* PixelFormat */ - DEPTH_COMPONENT : 0x1902, - ALPHA : 0x1906, - RGB : 0x1907, - RGBA : 0x1908, - LUMINANCE : 0x1909, - LUMINANCE_ALPHA : 0x190A, - - /* PixelType */ - /* UNSIGNED_BYTE */ - UNSIGNED_SHORT_4_4_4_4 : 0x8033, - UNSIGNED_SHORT_5_5_5_1 : 0x8034, - UNSIGNED_SHORT_5_6_5 : 0x8363, - - /* Shaders */ - FRAGMENT_SHADER : 0x8B30, - VERTEX_SHADER : 0x8B31, - MAX_VERTEX_ATTRIBS : 0x8869, - MAX_VERTEX_UNIFORM_VECTORS : 0x8DFB, - MAX_VARYING_VECTORS : 0x8DFC, - MAX_COMBINED_TEXTURE_IMAGE_UNITS : 0x8B4D, - MAX_VERTEX_TEXTURE_IMAGE_UNITS : 0x8B4C, - MAX_TEXTURE_IMAGE_UNITS : 0x8872, - MAX_FRAGMENT_UNIFORM_VECTORS : 0x8DFD, - SHADER_TYPE : 0x8B4F, - DELETE_STATUS : 0x8B80, - LINK_STATUS : 0x8B82, - VALIDATE_STATUS : 0x8B83, - ATTACHED_SHADERS : 0x8B85, - ACTIVE_UNIFORMS : 0x8B86, - ACTIVE_ATTRIBUTES : 0x8B89, - SHADING_LANGUAGE_VERSION : 0x8B8C, - CURRENT_PROGRAM : 0x8B8D, - - /* StencilFunction */ - NEVER : 0x0200, - LESS : 0x0201, - EQUAL : 0x0202, - LEQUAL : 0x0203, - GREATER : 0x0204, - NOTEQUAL : 0x0205, - GEQUAL : 0x0206, - ALWAYS : 0x0207, - - /* StencilOp */ - /* ZERO */ - KEEP : 0x1E00, - REPLACE : 0x1E01, - INCR : 0x1E02, - DECR : 0x1E03, - INVERT : 0x150A, - INCR_WRAP : 0x8507, - DECR_WRAP : 0x8508, - - /* StringName */ - VENDOR : 0x1F00, - RENDERER : 0x1F01, - VERSION : 0x1F02, - - /* TextureMagFilter */ - NEAREST : 0x2600, - LINEAR : 0x2601, - - /* TextureMinFilter */ - /* NEAREST */ - /* LINEAR */ - NEAREST_MIPMAP_NEAREST : 0x2700, - LINEAR_MIPMAP_NEAREST : 0x2701, - NEAREST_MIPMAP_LINEAR : 0x2702, - LINEAR_MIPMAP_LINEAR : 0x2703, - - /* TextureParameterName */ - TEXTURE_MAG_FILTER : 0x2800, - TEXTURE_MIN_FILTER : 0x2801, - TEXTURE_WRAP_S : 0x2802, - TEXTURE_WRAP_T : 0x2803, - - /* TextureTarget */ - TEXTURE_2D : 0x0DE1, - TEXTURE : 0x1702, - - TEXTURE_CUBE_MAP : 0x8513, - TEXTURE_BINDING_CUBE_MAP : 0x8514, - TEXTURE_CUBE_MAP_POSITIVE_X : 0x8515, - TEXTURE_CUBE_MAP_NEGATIVE_X : 0x8516, - TEXTURE_CUBE_MAP_POSITIVE_Y : 0x8517, - TEXTURE_CUBE_MAP_NEGATIVE_Y : 0x8518, - TEXTURE_CUBE_MAP_POSITIVE_Z : 0x8519, - TEXTURE_CUBE_MAP_NEGATIVE_Z : 0x851A, - MAX_CUBE_MAP_TEXTURE_SIZE : 0x851C, - - /* TextureUnit */ - TEXTURE0 : 0x84C0, - TEXTURE1 : 0x84C1, - TEXTURE2 : 0x84C2, - TEXTURE3 : 0x84C3, - TEXTURE4 : 0x84C4, - TEXTURE5 : 0x84C5, - TEXTURE6 : 0x84C6, - TEXTURE7 : 0x84C7, - TEXTURE8 : 0x84C8, - TEXTURE9 : 0x84C9, - TEXTURE10 : 0x84CA, - TEXTURE11 : 0x84CB, - TEXTURE12 : 0x84CC, - TEXTURE13 : 0x84CD, - TEXTURE14 : 0x84CE, - TEXTURE15 : 0x84CF, - TEXTURE16 : 0x84D0, - TEXTURE17 : 0x84D1, - TEXTURE18 : 0x84D2, - TEXTURE19 : 0x84D3, - TEXTURE20 : 0x84D4, - TEXTURE21 : 0x84D5, - TEXTURE22 : 0x84D6, - TEXTURE23 : 0x84D7, - TEXTURE24 : 0x84D8, - TEXTURE25 : 0x84D9, - TEXTURE26 : 0x84DA, - TEXTURE27 : 0x84DB, - TEXTURE28 : 0x84DC, - TEXTURE29 : 0x84DD, - TEXTURE30 : 0x84DE, - TEXTURE31 : 0x84DF, - ACTIVE_TEXTURE : 0x84E0, - - /* TextureWrapMode */ - REPEAT : 0x2901, - CLAMP_TO_EDGE : 0x812F, - MIRRORED_REPEAT : 0x8370, - - /* Uniform Types */ - FLOAT_VEC2 : 0x8B50, - FLOAT_VEC3 : 0x8B51, - FLOAT_VEC4 : 0x8B52, - INT_VEC2 : 0x8B53, - INT_VEC3 : 0x8B54, - INT_VEC4 : 0x8B55, - BOOL : 0x8B56, - BOOL_VEC2 : 0x8B57, - BOOL_VEC3 : 0x8B58, - BOOL_VEC4 : 0x8B59, - FLOAT_MAT2 : 0x8B5A, - FLOAT_MAT3 : 0x8B5B, - FLOAT_MAT4 : 0x8B5C, - SAMPLER_2D : 0x8B5E, - SAMPLER_CUBE : 0x8B60, - - /* Vertex Arrays */ - VERTEX_ATTRIB_ARRAY_ENABLED : 0x8622, - VERTEX_ATTRIB_ARRAY_SIZE : 0x8623, - VERTEX_ATTRIB_ARRAY_STRIDE : 0x8624, - VERTEX_ATTRIB_ARRAY_TYPE : 0x8625, - VERTEX_ATTRIB_ARRAY_NORMALIZED : 0x886A, - VERTEX_ATTRIB_ARRAY_POINTER : 0x8645, - VERTEX_ATTRIB_ARRAY_BUFFER_BINDING : 0x889F, - - /* Shader Source */ - COMPILE_STATUS : 0x8B81, - - /* Shader Precision-Specified Types */ - LOW_FLOAT : 0x8DF0, - MEDIUM_FLOAT : 0x8DF1, - HIGH_FLOAT : 0x8DF2, - LOW_INT : 0x8DF3, - MEDIUM_INT : 0x8DF4, - HIGH_INT : 0x8DF5, - - /* Framebuffer Object. */ - FRAMEBUFFER : 0x8D40, - RENDERBUFFER : 0x8D41, - - RGBA4 : 0x8056, - RGB5_A1 : 0x8057, - RGB565 : 0x8D62, - DEPTH_COMPONENT16 : 0x81A5, - STENCIL_INDEX : 0x1901, - STENCIL_INDEX8 : 0x8D48, - DEPTH_STENCIL : 0x84F9, - - RENDERBUFFER_WIDTH : 0x8D42, - RENDERBUFFER_HEIGHT : 0x8D43, - RENDERBUFFER_INTERNAL_FORMAT : 0x8D44, - RENDERBUFFER_RED_SIZE : 0x8D50, - RENDERBUFFER_GREEN_SIZE : 0x8D51, - RENDERBUFFER_BLUE_SIZE : 0x8D52, - RENDERBUFFER_ALPHA_SIZE : 0x8D53, - RENDERBUFFER_DEPTH_SIZE : 0x8D54, - RENDERBUFFER_STENCIL_SIZE : 0x8D55, - - FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE : 0x8CD0, - FRAMEBUFFER_ATTACHMENT_OBJECT_NAME : 0x8CD1, - FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL : 0x8CD2, - FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE : 0x8CD3, - - COLOR_ATTACHMENT0 : 0x8CE0, - DEPTH_ATTACHMENT : 0x8D00, - STENCIL_ATTACHMENT : 0x8D20, - DEPTH_STENCIL_ATTACHMENT : 0x821A, - - NONE : 0, - - FRAMEBUFFER_COMPLETE : 0x8CD5, - FRAMEBUFFER_INCOMPLETE_ATTACHMENT : 0x8CD6, - FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT : 0x8CD7, - FRAMEBUFFER_INCOMPLETE_DIMENSIONS : 0x8CD9, - FRAMEBUFFER_UNSUPPORTED : 0x8CDD, - - FRAMEBUFFER_BINDING : 0x8CA6, - RENDERBUFFER_BINDING : 0x8CA7, - MAX_RENDERBUFFER_SIZE : 0x84E8, - - INVALID_FRAMEBUFFER_OPERATION : 0x0506, - - /* WebGL-specific enums */ - UNPACK_FLIP_Y_WEBGL : 0x9240, - UNPACK_PREMULTIPLY_ALPHA_WEBGL : 0x9241, - CONTEXT_LOST_WEBGL : 0x9242, - UNPACK_COLORSPACE_CONVERSION_WEBGL : 0x9243, - BROWSER_DEFAULT_WEBGL : 0x9244, - - items: {}, - id: 0, - getExtension: function() { return 1 }, - createBuffer: function() { - var id = this.id++; - this.items[id] = { - which: 'buffer', - }; - return id; - }, - deleteBuffer: function(){}, - bindBuffer: function(){}, - bufferData: function(){}, - getParameter: function(pname) { - switch(pname) { - case /* GL_VENDOR */ 0x1F00: return 'FakeShellGLVendor'; - case /* GL_RENDERER */ 0x1F01: return 'FakeShellGLRenderer'; - case /* GL_VERSION */ 0x1F02: return '0.0.1'; - case /* GL_MAX_TEXTURE_SIZE */ 0x0D33: return 16384; - case /* GL_MAX_CUBE_MAP_TEXTURE_SIZE */ 0x851C: return 16384; - case /* GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT */ 0x84FF: return 16; - case /* GL_MAX_TEXTURE_IMAGE_UNITS_NV */ 0x8872: return 16; - case /* GL_MAX_VERTEX_UNIFORM_VECTORS */ 0x8DFB: return 4096; - case /* GL_MAX_FRAGMENT_UNIFORM_VECTORS */ 0x8DFD: return 4096; - case /* GL_MAX_VARYING_VECTORS */ 0x8DFC: return 32; - case /* GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS */ 0x8B4D: return 32; - default: console.log('getParameter ' + pname + '?'); return 0; - } - }, - getSupportedExtensions: function() { - return ["OES_texture_float", "OES_standard_derivatives", "EXT_texture_filter_anisotropic", "MOZ_EXT_texture_filter_anisotropic", "MOZ_WEBGL_lose_context", "MOZ_WEBGL_compressed_texture_s3tc", "MOZ_WEBGL_depth_texture"]; - }, - createShader: function(type) { - var id = this.id++; - this.items[id] = { - which: 'shader', - type: type, - }; - return id; - }, - getShaderParameter: function(shader, pname) { - switch(pname) { - case /* GL_SHADER_TYPE */ 0x8B4F: return this.items[shader].type; - case /* GL_COMPILE_STATUS */ 0x8B81: return true; - default: throw 'getShaderParameter ' + pname; - } - }, - shaderSource: function(){}, - compileShader: function(){}, - createProgram: function() { - var id = this.id++; - this.items[id] = { - which: 'program', - shaders: [], - }; - return id; - }, - attachShader: function(program, shader) { - this.items[program].shaders.push(shader); - }, - bindAttribLocation: function(){}, - linkProgram: function(){}, - getProgramParameter: function(program, pname) { - switch(pname) { - case /* LINK_STATUS */ 0x8B82: return true; - case /* ACTIVE_UNIFORMS */ 0x8B86: return 4; - default: throw 'getProgramParameter ' + pname; - } - }, - deleteShader: function(){}, - deleteProgram: function(){}, - viewport: function(){}, - clearColor: function(){}, - clearDepth: function(){}, - depthFunc: function(){}, - enable: function(){}, - disable: function(){}, - frontFace: function(){}, - cullFace: function(){}, - activeTexture: function(){}, - createTexture: function() { - var id = this.id++; - this.items[id] = { - which: 'texture', - }; - return id; - }, - deleteTexture: function(){}, - boundTextures: {}, - bindTexture: function(target, texture) { - this.boundTextures[target] = texture; - }, - texParameteri: function(){}, - pixelStorei: function(){}, - texImage2D: function(){}, - compressedTexImage2D: function(){}, - useProgram: function(){}, - getUniformLocation: function() { - return null; - }, - getActiveUniform: function(program, index) { - return { - size: 1, - type: /* INT_VEC3 */ 0x8B54, - name: 'activeUniform' + index, - }; - }, - clear: function(){}, - uniform4fv: function(){}, - uniform1i: function(){}, - getAttribLocation: function() { return 1 }, - vertexAttribPointer: function(){}, - enableVertexAttribArray: function(){}, - disableVertexAttribArray: function(){}, - drawElements: function(){}, - drawArrays: function(){}, - depthMask: function(){}, - depthRange: function(){}, - bufferSubData: function(){}, - blendFunc: function(){}, - createFramebuffer: function() { - var id = this.id++; - this.items[id] = { - which: 'framebuffer', - shaders: [], - }; - return id; - }, - bindFramebuffer: function(){}, - framebufferTexture2D: function(){}, - checkFramebufferStatus: function() { - return /* FRAMEBUFFER_COMPLETE */ 0x8CD5; - }, - createRenderbuffer: function() { - var id = this.id++; - this.items[id] = { - which: 'renderbuffer', - shaders: [], - }; - return id; - }, - bindRenderbuffer: function(){}, - renderbufferStorage: function(){}, - framebufferRenderbuffer: function(){}, - scissor: function(){}, - colorMask: function(){}, - lineWidth: function(){}, - }; - } - case '2d': { - return { - drawImage: function(){}, - getImageData: function(x, y, w, h) { - return { - width: w, - height: h, - data: new Uint8ClampedArray(w*h), - }; - }, - save: function(){}, - restore: function(){}, - fillRect: function(){}, - measureText: function() { return 10 }, - fillText: function(){}, - }; - } - default: throw 'canvas.getContext: ' + which; - } - }, - requestPointerLock: function() { - document.pointerLockElement = document.getElementById('canvas'); - window.setTimeout(function() { - document.callEventListeners('pointerlockchange'); - }); - }, - exitPointerLock: function(){}, - style: {}, - eventListeners: {}, - addEventListener: document.addEventListener, - callEventListeners: document.callEventListeners, - requestFullScreen: function() { - document.fullscreenElement = document.getElementById('canvas'); - window.setTimeout(function() { - document.callEventListeners('fullscreenchange'); - }); - }, - offsetTop: 0, - offsetLeft: 0, - }; + return this.canvas = headlessCanvas(); } case 'status-text': case 'progress': { return {}; @@ -729,9 +120,9 @@ var document = { case 'script': { var ret = {}; window.setTimeout(function() { - print('loading script: ' + ret.src); + headlessPrint('loading script: ' + ret.src); load(ret.src); - print(' script loaded.'); + headlessPrint(' script loaded.'); if (ret.onload) { window.setTimeout(function() { ret.onload(); // yeah yeah this might vanish @@ -771,16 +162,14 @@ var document = { var alert = function(x) { print(x); }; -var originalDateNow = Date.now; var performance = { now: function() { - return originalDateNow.call(Date); + return Date.now(); }, }; function fixPath(path) { if (path[0] == '/') path = path.substring(1); - var dirsToDrop = %d; // go back to root dir if first_js is in a subdir - for (var i = 0; i < dirsToDrop; i++) { + for (var i = 0; i < window.dirsToDrop; i++) { path = '../' + path; } return path @@ -835,11 +224,11 @@ var Worker = function(workerPath) { workerPath = fixPath(workerPath); var workerCode = read(workerPath); workerCode = workerCode.replace(/Module/g, 'zzModuleyy' + (Worker.id++)). // prevent collision with the global Module object. Note that this becomes global, so we need unique ids - replace(/Date.now/g, 'Recorder.dnow'). // recorded values are just for the "main thread" - workers were not recorded, and should not consume - replace(/performance.now/g, 'Recorder.pnow'). - replace(/Math.random/g, 'Recorder.random'). + //replace(/Date.now/g, 'Recorder.dnow'). // recorded values are just for the "main thread" - workers were not recorded, and should not consume + //replace(/performance.now/g, 'Recorder.pnow'). + //replace(/Math.random/g, 'Recorder.random'). replace(/\nonmessage = /, '\nvar onmessage = '); // workers commonly do "onmessage = ", we need to varify that to sandbox - print('loading worker ' + workerPath + ' : ' + workerCode.substring(0, 50)); + headlessPrint('loading worker ' + workerPath + ' : ' + workerCode.substring(0, 50)); eval(workerCode); // will implement onmessage() function duplicateJSON(json) { @@ -854,18 +243,18 @@ var Worker = function(workerPath) { this.terminate = function(){}; this.postMessage = function(msg) { msg.messageId = Worker.messageId++; - print('main thread sending message ' + msg.messageId + ' to worker ' + workerPath); + headlessPrint('main thread sending message ' + msg.messageId + ' to worker ' + workerPath); window.setTimeout(function() { - print('worker ' + workerPath + ' receiving message ' + msg.messageId); + headlessPrint('worker ' + workerPath + ' receiving message ' + msg.messageId); onmessage({ data: duplicateJSON(msg) }); }); }; var thisWorker = this; var postMessage = function(msg) { msg.messageId = Worker.messageId++; - print('worker ' + workerPath + ' sending message ' + msg.messageId); + headlessPrint('worker ' + workerPath + ' sending message ' + msg.messageId); window.setTimeout(function() { - print('main thread receiving message ' + msg.messageId + ' from ' + workerPath); + headlessPrint('main thread receiving message ' + msg.messageId + ' from ' + workerPath); thisWorker.onmessage({ data: duplicateJSON(msg) }); }); }; diff --git a/src/headlessCanvas.js b/src/headlessCanvas.js new file mode 100644 index 0000000000000..4951aed8d8e2a --- /dev/null +++ b/src/headlessCanvas.js @@ -0,0 +1,618 @@ +function headlessCanvas() { + return { + headless: true, + getContext: function(which) { + switch(which) { + case 'experimental-webgl': { + return { + /* ClearBufferMask */ + DEPTH_BUFFER_BIT : 0x00000100, + STENCIL_BUFFER_BIT : 0x00000400, + COLOR_BUFFER_BIT : 0x00004000, + + /* BeginMode */ + POINTS : 0x0000, + LINES : 0x0001, + LINE_LOOP : 0x0002, + LINE_STRIP : 0x0003, + TRIANGLES : 0x0004, + TRIANGLE_STRIP : 0x0005, + TRIANGLE_FAN : 0x0006, + + /* AlphaFunction (not supported in ES20) */ + /* NEVER */ + /* LESS */ + /* EQUAL */ + /* LEQUAL */ + /* GREATER */ + /* NOTEQUAL */ + /* GEQUAL */ + /* ALWAYS */ + + /* BlendingFactorDest */ + ZERO : 0, + ONE : 1, + SRC_COLOR : 0x0300, + ONE_MINUS_SRC_COLOR : 0x0301, + SRC_ALPHA : 0x0302, + ONE_MINUS_SRC_ALPHA : 0x0303, + DST_ALPHA : 0x0304, + ONE_MINUS_DST_ALPHA : 0x0305, + + /* BlendingFactorSrc */ + /* ZERO */ + /* ONE */ + DST_COLOR : 0x0306, + ONE_MINUS_DST_COLOR : 0x0307, + SRC_ALPHA_SATURATE : 0x0308, + /* SRC_ALPHA */ + /* ONE_MINUS_SRC_ALPHA */ + /* DST_ALPHA */ + /* ONE_MINUS_DST_ALPHA */ + + /* BlendEquationSeparate */ + FUNC_ADD : 0x8006, + BLEND_EQUATION : 0x8009, + BLEND_EQUATION_RGB : 0x8009, /* same as BLEND_EQUATION */ + BLEND_EQUATION_ALPHA : 0x883D, + + /* BlendSubtract */ + FUNC_SUBTRACT : 0x800A, + FUNC_REVERSE_SUBTRACT : 0x800B, + + /* Separate Blend Functions */ + BLEND_DST_RGB : 0x80C8, + BLEND_SRC_RGB : 0x80C9, + BLEND_DST_ALPHA : 0x80CA, + BLEND_SRC_ALPHA : 0x80CB, + CONSTANT_COLOR : 0x8001, + ONE_MINUS_CONSTANT_COLOR : 0x8002, + CONSTANT_ALPHA : 0x8003, + ONE_MINUS_CONSTANT_ALPHA : 0x8004, + BLEND_COLOR : 0x8005, + + /* Buffer Objects */ + ARRAY_BUFFER : 0x8892, + ELEMENT_ARRAY_BUFFER : 0x8893, + ARRAY_BUFFER_BINDING : 0x8894, + ELEMENT_ARRAY_BUFFER_BINDING : 0x8895, + + STREAM_DRAW : 0x88E0, + STATIC_DRAW : 0x88E4, + DYNAMIC_DRAW : 0x88E8, + + BUFFER_SIZE : 0x8764, + BUFFER_USAGE : 0x8765, + + CURRENT_VERTEX_ATTRIB : 0x8626, + + /* CullFaceMode */ + FRONT : 0x0404, + BACK : 0x0405, + FRONT_AND_BACK : 0x0408, + + /* DepthFunction */ + /* NEVER */ + /* LESS */ + /* EQUAL */ + /* LEQUAL */ + /* GREATER */ + /* NOTEQUAL */ + /* GEQUAL */ + /* ALWAYS */ + + /* EnableCap */ + /* TEXTURE_2D */ + CULL_FACE : 0x0B44, + BLEND : 0x0BE2, + DITHER : 0x0BD0, + STENCIL_TEST : 0x0B90, + DEPTH_TEST : 0x0B71, + SCISSOR_TEST : 0x0C11, + POLYGON_OFFSET_FILL : 0x8037, + SAMPLE_ALPHA_TO_COVERAGE : 0x809E, + SAMPLE_COVERAGE : 0x80A0, + + /* ErrorCode */ + NO_ERROR : 0, + INVALID_ENUM : 0x0500, + INVALID_VALUE : 0x0501, + INVALID_OPERATION : 0x0502, + OUT_OF_MEMORY : 0x0505, + + /* FrontFaceDirection */ + CW : 0x0900, + CCW : 0x0901, + + /* GetPName */ + LINE_WIDTH : 0x0B21, + ALIASED_POINT_SIZE_RANGE : 0x846D, + ALIASED_LINE_WIDTH_RANGE : 0x846E, + CULL_FACE_MODE : 0x0B45, + FRONT_FACE : 0x0B46, + DEPTH_RANGE : 0x0B70, + DEPTH_WRITEMASK : 0x0B72, + DEPTH_CLEAR_VALUE : 0x0B73, + DEPTH_FUNC : 0x0B74, + STENCIL_CLEAR_VALUE : 0x0B91, + STENCIL_FUNC : 0x0B92, + STENCIL_FAIL : 0x0B94, + STENCIL_PASS_DEPTH_FAIL : 0x0B95, + STENCIL_PASS_DEPTH_PASS : 0x0B96, + STENCIL_REF : 0x0B97, + STENCIL_VALUE_MASK : 0x0B93, + STENCIL_WRITEMASK : 0x0B98, + STENCIL_BACK_FUNC : 0x8800, + STENCIL_BACK_FAIL : 0x8801, + STENCIL_BACK_PASS_DEPTH_FAIL : 0x8802, + STENCIL_BACK_PASS_DEPTH_PASS : 0x8803, + STENCIL_BACK_REF : 0x8CA3, + STENCIL_BACK_VALUE_MASK : 0x8CA4, + STENCIL_BACK_WRITEMASK : 0x8CA5, + VIEWPORT : 0x0BA2, + SCISSOR_BOX : 0x0C10, + /* SCISSOR_TEST */ + COLOR_CLEAR_VALUE : 0x0C22, + COLOR_WRITEMASK : 0x0C23, + UNPACK_ALIGNMENT : 0x0CF5, + PACK_ALIGNMENT : 0x0D05, + MAX_TEXTURE_SIZE : 0x0D33, + MAX_VIEWPORT_DIMS : 0x0D3A, + SUBPIXEL_BITS : 0x0D50, + RED_BITS : 0x0D52, + GREEN_BITS : 0x0D53, + BLUE_BITS : 0x0D54, + ALPHA_BITS : 0x0D55, + DEPTH_BITS : 0x0D56, + STENCIL_BITS : 0x0D57, + POLYGON_OFFSET_UNITS : 0x2A00, + /* POLYGON_OFFSET_FILL */ + POLYGON_OFFSET_FACTOR : 0x8038, + TEXTURE_BINDING_2D : 0x8069, + SAMPLE_BUFFERS : 0x80A8, + SAMPLES : 0x80A9, + SAMPLE_COVERAGE_VALUE : 0x80AA, + SAMPLE_COVERAGE_INVERT : 0x80AB, + + /* GetTextureParameter */ + /* TEXTURE_MAG_FILTER */ + /* TEXTURE_MIN_FILTER */ + /* TEXTURE_WRAP_S */ + /* TEXTURE_WRAP_T */ + + COMPRESSED_TEXTURE_FORMATS : 0x86A3, + + /* HintMode */ + DONT_CARE : 0x1100, + FASTEST : 0x1101, + NICEST : 0x1102, + + /* HintTarget */ + GENERATE_MIPMAP_HINT : 0x8192, + + /* DataType */ + BYTE : 0x1400, + UNSIGNED_BYTE : 0x1401, + SHORT : 0x1402, + UNSIGNED_SHORT : 0x1403, + INT : 0x1404, + UNSIGNED_INT : 0x1405, + FLOAT : 0x1406, + + /* PixelFormat */ + DEPTH_COMPONENT : 0x1902, + ALPHA : 0x1906, + RGB : 0x1907, + RGBA : 0x1908, + LUMINANCE : 0x1909, + LUMINANCE_ALPHA : 0x190A, + + /* PixelType */ + /* UNSIGNED_BYTE */ + UNSIGNED_SHORT_4_4_4_4 : 0x8033, + UNSIGNED_SHORT_5_5_5_1 : 0x8034, + UNSIGNED_SHORT_5_6_5 : 0x8363, + + /* Shaders */ + FRAGMENT_SHADER : 0x8B30, + VERTEX_SHADER : 0x8B31, + MAX_VERTEX_ATTRIBS : 0x8869, + MAX_VERTEX_UNIFORM_VECTORS : 0x8DFB, + MAX_VARYING_VECTORS : 0x8DFC, + MAX_COMBINED_TEXTURE_IMAGE_UNITS : 0x8B4D, + MAX_VERTEX_TEXTURE_IMAGE_UNITS : 0x8B4C, + MAX_TEXTURE_IMAGE_UNITS : 0x8872, + MAX_FRAGMENT_UNIFORM_VECTORS : 0x8DFD, + SHADER_TYPE : 0x8B4F, + DELETE_STATUS : 0x8B80, + LINK_STATUS : 0x8B82, + VALIDATE_STATUS : 0x8B83, + ATTACHED_SHADERS : 0x8B85, + ACTIVE_UNIFORMS : 0x8B86, + ACTIVE_ATTRIBUTES : 0x8B89, + SHADING_LANGUAGE_VERSION : 0x8B8C, + CURRENT_PROGRAM : 0x8B8D, + + /* StencilFunction */ + NEVER : 0x0200, + LESS : 0x0201, + EQUAL : 0x0202, + LEQUAL : 0x0203, + GREATER : 0x0204, + NOTEQUAL : 0x0205, + GEQUAL : 0x0206, + ALWAYS : 0x0207, + + /* StencilOp */ + /* ZERO */ + KEEP : 0x1E00, + REPLACE : 0x1E01, + INCR : 0x1E02, + DECR : 0x1E03, + INVERT : 0x150A, + INCR_WRAP : 0x8507, + DECR_WRAP : 0x8508, + + /* StringName */ + VENDOR : 0x1F00, + RENDERER : 0x1F01, + VERSION : 0x1F02, + + /* TextureMagFilter */ + NEAREST : 0x2600, + LINEAR : 0x2601, + + /* TextureMinFilter */ + /* NEAREST */ + /* LINEAR */ + NEAREST_MIPMAP_NEAREST : 0x2700, + LINEAR_MIPMAP_NEAREST : 0x2701, + NEAREST_MIPMAP_LINEAR : 0x2702, + LINEAR_MIPMAP_LINEAR : 0x2703, + + /* TextureParameterName */ + TEXTURE_MAG_FILTER : 0x2800, + TEXTURE_MIN_FILTER : 0x2801, + TEXTURE_WRAP_S : 0x2802, + TEXTURE_WRAP_T : 0x2803, + + /* TextureTarget */ + TEXTURE_2D : 0x0DE1, + TEXTURE : 0x1702, + + TEXTURE_CUBE_MAP : 0x8513, + TEXTURE_BINDING_CUBE_MAP : 0x8514, + TEXTURE_CUBE_MAP_POSITIVE_X : 0x8515, + TEXTURE_CUBE_MAP_NEGATIVE_X : 0x8516, + TEXTURE_CUBE_MAP_POSITIVE_Y : 0x8517, + TEXTURE_CUBE_MAP_NEGATIVE_Y : 0x8518, + TEXTURE_CUBE_MAP_POSITIVE_Z : 0x8519, + TEXTURE_CUBE_MAP_NEGATIVE_Z : 0x851A, + MAX_CUBE_MAP_TEXTURE_SIZE : 0x851C, + + /* TextureUnit */ + TEXTURE0 : 0x84C0, + TEXTURE1 : 0x84C1, + TEXTURE2 : 0x84C2, + TEXTURE3 : 0x84C3, + TEXTURE4 : 0x84C4, + TEXTURE5 : 0x84C5, + TEXTURE6 : 0x84C6, + TEXTURE7 : 0x84C7, + TEXTURE8 : 0x84C8, + TEXTURE9 : 0x84C9, + TEXTURE10 : 0x84CA, + TEXTURE11 : 0x84CB, + TEXTURE12 : 0x84CC, + TEXTURE13 : 0x84CD, + TEXTURE14 : 0x84CE, + TEXTURE15 : 0x84CF, + TEXTURE16 : 0x84D0, + TEXTURE17 : 0x84D1, + TEXTURE18 : 0x84D2, + TEXTURE19 : 0x84D3, + TEXTURE20 : 0x84D4, + TEXTURE21 : 0x84D5, + TEXTURE22 : 0x84D6, + TEXTURE23 : 0x84D7, + TEXTURE24 : 0x84D8, + TEXTURE25 : 0x84D9, + TEXTURE26 : 0x84DA, + TEXTURE27 : 0x84DB, + TEXTURE28 : 0x84DC, + TEXTURE29 : 0x84DD, + TEXTURE30 : 0x84DE, + TEXTURE31 : 0x84DF, + ACTIVE_TEXTURE : 0x84E0, + + /* TextureWrapMode */ + REPEAT : 0x2901, + CLAMP_TO_EDGE : 0x812F, + MIRRORED_REPEAT : 0x8370, + + /* Uniform Types */ + FLOAT_VEC2 : 0x8B50, + FLOAT_VEC3 : 0x8B51, + FLOAT_VEC4 : 0x8B52, + INT_VEC2 : 0x8B53, + INT_VEC3 : 0x8B54, + INT_VEC4 : 0x8B55, + BOOL : 0x8B56, + BOOL_VEC2 : 0x8B57, + BOOL_VEC3 : 0x8B58, + BOOL_VEC4 : 0x8B59, + FLOAT_MAT2 : 0x8B5A, + FLOAT_MAT3 : 0x8B5B, + FLOAT_MAT4 : 0x8B5C, + SAMPLER_2D : 0x8B5E, + SAMPLER_CUBE : 0x8B60, + + /* Vertex Arrays */ + VERTEX_ATTRIB_ARRAY_ENABLED : 0x8622, + VERTEX_ATTRIB_ARRAY_SIZE : 0x8623, + VERTEX_ATTRIB_ARRAY_STRIDE : 0x8624, + VERTEX_ATTRIB_ARRAY_TYPE : 0x8625, + VERTEX_ATTRIB_ARRAY_NORMALIZED : 0x886A, + VERTEX_ATTRIB_ARRAY_POINTER : 0x8645, + VERTEX_ATTRIB_ARRAY_BUFFER_BINDING : 0x889F, + + /* Shader Source */ + COMPILE_STATUS : 0x8B81, + + /* Shader Precision-Specified Types */ + LOW_FLOAT : 0x8DF0, + MEDIUM_FLOAT : 0x8DF1, + HIGH_FLOAT : 0x8DF2, + LOW_INT : 0x8DF3, + MEDIUM_INT : 0x8DF4, + HIGH_INT : 0x8DF5, + + /* Framebuffer Object. */ + FRAMEBUFFER : 0x8D40, + RENDERBUFFER : 0x8D41, + + RGBA4 : 0x8056, + RGB5_A1 : 0x8057, + RGB565 : 0x8D62, + DEPTH_COMPONENT16 : 0x81A5, + STENCIL_INDEX : 0x1901, + STENCIL_INDEX8 : 0x8D48, + DEPTH_STENCIL : 0x84F9, + + RENDERBUFFER_WIDTH : 0x8D42, + RENDERBUFFER_HEIGHT : 0x8D43, + RENDERBUFFER_INTERNAL_FORMAT : 0x8D44, + RENDERBUFFER_RED_SIZE : 0x8D50, + RENDERBUFFER_GREEN_SIZE : 0x8D51, + RENDERBUFFER_BLUE_SIZE : 0x8D52, + RENDERBUFFER_ALPHA_SIZE : 0x8D53, + RENDERBUFFER_DEPTH_SIZE : 0x8D54, + RENDERBUFFER_STENCIL_SIZE : 0x8D55, + + FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE : 0x8CD0, + FRAMEBUFFER_ATTACHMENT_OBJECT_NAME : 0x8CD1, + FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL : 0x8CD2, + FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE : 0x8CD3, + + COLOR_ATTACHMENT0 : 0x8CE0, + DEPTH_ATTACHMENT : 0x8D00, + STENCIL_ATTACHMENT : 0x8D20, + DEPTH_STENCIL_ATTACHMENT : 0x821A, + + NONE : 0, + + FRAMEBUFFER_COMPLETE : 0x8CD5, + FRAMEBUFFER_INCOMPLETE_ATTACHMENT : 0x8CD6, + FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT : 0x8CD7, + FRAMEBUFFER_INCOMPLETE_DIMENSIONS : 0x8CD9, + FRAMEBUFFER_UNSUPPORTED : 0x8CDD, + + FRAMEBUFFER_BINDING : 0x8CA6, + RENDERBUFFER_BINDING : 0x8CA7, + MAX_RENDERBUFFER_SIZE : 0x84E8, + + INVALID_FRAMEBUFFER_OPERATION : 0x0506, + + /* WebGL-specific enums */ + UNPACK_FLIP_Y_WEBGL : 0x9240, + UNPACK_PREMULTIPLY_ALPHA_WEBGL : 0x9241, + CONTEXT_LOST_WEBGL : 0x9242, + UNPACK_COLORSPACE_CONVERSION_WEBGL : 0x9243, + BROWSER_DEFAULT_WEBGL : 0x9244, + + items: {}, + id: 0, + getExtension: function() { return 1 }, + createBuffer: function() { + var id = this.id++; + this.items[id] = { + which: 'buffer', + }; + return id; + }, + deleteBuffer: function(){}, + bindBuffer: function(){}, + bufferData: function(){}, + getParameter: function(pname) { + switch(pname) { + case /* GL_VENDOR */ 0x1F00: return 'FakeShellGLVendor'; + case /* GL_RENDERER */ 0x1F01: return 'FakeShellGLRenderer'; + case /* GL_VERSION */ 0x1F02: return '0.0.1'; + case /* GL_MAX_TEXTURE_SIZE */ 0x0D33: return 16384; + case /* GL_MAX_CUBE_MAP_TEXTURE_SIZE */ 0x851C: return 16384; + case /* GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT */ 0x84FF: return 16; + case /* GL_MAX_TEXTURE_IMAGE_UNITS_NV */ 0x8872: return 16; + case /* GL_MAX_VERTEX_UNIFORM_VECTORS */ 0x8DFB: return 4096; + case /* GL_MAX_FRAGMENT_UNIFORM_VECTORS */ 0x8DFD: return 4096; + case /* GL_MAX_VARYING_VECTORS */ 0x8DFC: return 32; + case /* GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS */ 0x8B4D: return 32; + default: console.log('getParameter ' + pname + '?'); return 0; + } + }, + getSupportedExtensions: function() { + return ["OES_texture_float", "OES_standard_derivatives", "EXT_texture_filter_anisotropic", "MOZ_EXT_texture_filter_anisotropic", "MOZ_WEBGL_lose_context", "MOZ_WEBGL_compressed_texture_s3tc", "MOZ_WEBGL_depth_texture"]; + }, + createShader: function(type) { + var id = this.id++; + this.items[id] = { + which: 'shader', + type: type, + }; + return id; + }, + getShaderParameter: function(shader, pname) { + switch(pname) { + case /* GL_SHADER_TYPE */ 0x8B4F: return this.items[shader].type; + case /* GL_COMPILE_STATUS */ 0x8B81: return true; + default: throw 'getShaderParameter ' + pname; + } + }, + shaderSource: function(){}, + compileShader: function(){}, + createProgram: function() { + var id = this.id++; + this.items[id] = { + which: 'program', + shaders: [], + }; + return id; + }, + attachShader: function(program, shader) { + this.items[program].shaders.push(shader); + }, + bindAttribLocation: function(){}, + linkProgram: function(){}, + getProgramParameter: function(program, pname) { + switch(pname) { + case /* LINK_STATUS */ 0x8B82: return true; + case /* ACTIVE_UNIFORMS */ 0x8B86: return 4; + default: throw 'getProgramParameter ' + pname; + } + }, + deleteShader: function(){}, + deleteProgram: function(){}, + viewport: function(){}, + clearColor: function(){}, + clearDepth: function(){}, + depthFunc: function(){}, + enable: function(){}, + disable: function(){}, + frontFace: function(){}, + cullFace: function(){}, + activeTexture: function(){}, + createTexture: function() { + var id = this.id++; + this.items[id] = { + which: 'texture', + }; + return id; + }, + deleteTexture: function(){}, + boundTextures: {}, + bindTexture: function(target, texture) { + this.boundTextures[target] = texture; + }, + texParameteri: function(){}, + pixelStorei: function(){}, + texImage2D: function(){}, + compressedTexImage2D: function(){}, + useProgram: function(){}, + getUniformLocation: function() { + return null; + }, + getActiveUniform: function(program, index) { + return { + size: 1, + type: /* INT_VEC3 */ 0x8B54, + name: 'activeUniform' + index, + }; + }, + clear: function(){}, + uniform4fv: function(){}, + uniform1i: function(){}, + getAttribLocation: function() { return 1 }, + vertexAttribPointer: function(){}, + enableVertexAttribArray: function(){}, + disableVertexAttribArray: function(){}, + drawElements: function(){}, + drawArrays: function(){}, + depthMask: function(){}, + depthRange: function(){}, + bufferSubData: function(){}, + blendFunc: function(){}, + createFramebuffer: function() { + var id = this.id++; + this.items[id] = { + which: 'framebuffer', + shaders: [], + }; + return id; + }, + bindFramebuffer: function(){}, + framebufferTexture2D: function(){}, + checkFramebufferStatus: function() { + return /* FRAMEBUFFER_COMPLETE */ 0x8CD5; + }, + createRenderbuffer: function() { + var id = this.id++; + this.items[id] = { + which: 'renderbuffer', + shaders: [], + }; + return id; + }, + bindRenderbuffer: function(){}, + renderbufferStorage: function(){}, + framebufferRenderbuffer: function(){}, + scissor: function(){}, + colorMask: function(){}, + lineWidth: function(){}, + vertexAttrib4fv: function(){}, + }; + } + case '2d': { + return { + drawImage: function(){}, + getImageData: function(x, y, w, h) { + return { + width: w, + height: h, + data: new Uint8ClampedArray(w*h), + }; + }, + save: function(){}, + restore: function(){}, + fillRect: function(){}, + measureText: function() { return 10 }, + fillText: function(){}, + }; + } + default: throw 'canvas.getContext: ' + which; + } + }, + requestPointerLock: function() { + document.pointerLockElement = document.getElementById('canvas'); + window.setTimeout(function() { + document.callEventListeners('pointerlockchange'); + }); + }, + exitPointerLock: function(){}, + style: {}, + eventListeners: {}, + addEventListener: function(){}, + requestFullScreen: function() { + document.fullscreenElement = document.getElementById('canvas'); + window.setTimeout(function() { + document.callEventListeners('fullscreenchange'); + }); + }, + offsetTop: 0, + offsetLeft: 0, + // generics + classList: { + add: function(){}, + remove: function(){}, + }, + }; +} + diff --git a/src/jsifier.js b/src/jsifier.js index c7742288c7093..683b1874bee3e 100644 --- a/src/jsifier.js +++ b/src/jsifier.js @@ -1806,6 +1806,8 @@ function JSify(data, functionsOnly, givenFunctions) { } if (HEADLESS) { print('if (!ENVIRONMENT_IS_WEB) {'); + print(read('headlessCanvas.js')); + print('\n'); print(read('headless.js').replace("'%s'", "'http://emscripten.org'").replace("'?%s'", "''").replace("'?%s'", "'/'").replace('%s,', 'null,').replace('%d', '0')); print('}'); } From a0c1f8c193e4d5c318f04b886c52bc3e8ab06980 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 20 Aug 2013 13:53:31 -0700 Subject: [PATCH 108/112] todo comment on TextDecoder --- src/preamble.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/preamble.js b/src/preamble.js index 0f3f52c9b8e7b..4cae05a6465a5 100644 --- a/src/preamble.js +++ b/src/preamble.js @@ -524,6 +524,7 @@ function allocate(slab, types, allocator, ptr) { Module['allocate'] = allocate; function Pointer_stringify(ptr, /* optional */ length) { + // TODO: use TextDecoder // Find the length, and check for UTF while doing so var hasUtf = false; var t; From bd05b4d4f8c805c45630977547b0b01c04b1ce18 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 21 Aug 2013 11:29:11 -0700 Subject: [PATCH 109/112] remove some function indentation in asm.js block --- emscripten.py | 86 +++++++++++++++++++++++++-------------------------- 1 file changed, 43 insertions(+), 43 deletions(-) diff --git a/emscripten.py b/emscripten.py index a156ca73b098c..c5e235d8abadc 100755 --- a/emscripten.py +++ b/emscripten.py @@ -552,52 +552,52 @@ def math_fix(g): ''' + ''.join([''' var tempRet%d = 0;''' % i for i in range(10)]) + '\n' + asm_global_funcs + ''' // EMSCRIPTEN_START_FUNCS - function stackAlloc(size) { - size = size|0; - var ret = 0; - ret = STACKTOP; - STACKTOP = (STACKTOP + size)|0; +function stackAlloc(size) { + size = size|0; + var ret = 0; + ret = STACKTOP; + STACKTOP = (STACKTOP + size)|0; ''' + ('STACKTOP = ((STACKTOP + 3)>>2)<<2;' if settings['TARGET_X86'] else 'STACKTOP = ((STACKTOP + 7)>>3)<<3;') + ''' - return ret|0; - } - function stackSave() { - return STACKTOP|0; - } - function stackRestore(top) { - top = top|0; - STACKTOP = top; - } - function setThrew(threw, value) { - threw = threw|0; - value = value|0; - if ((__THREW__|0) == 0) { - __THREW__ = threw; - threwValue = value; - } - } - function copyTempFloat(ptr) { - ptr = ptr|0; - HEAP8[tempDoublePtr] = HEAP8[ptr]; - HEAP8[tempDoublePtr+1|0] = HEAP8[ptr+1|0]; - HEAP8[tempDoublePtr+2|0] = HEAP8[ptr+2|0]; - HEAP8[tempDoublePtr+3|0] = HEAP8[ptr+3|0]; - } - function copyTempDouble(ptr) { - ptr = ptr|0; - HEAP8[tempDoublePtr] = HEAP8[ptr]; - HEAP8[tempDoublePtr+1|0] = HEAP8[ptr+1|0]; - HEAP8[tempDoublePtr+2|0] = HEAP8[ptr+2|0]; - HEAP8[tempDoublePtr+3|0] = HEAP8[ptr+3|0]; - HEAP8[tempDoublePtr+4|0] = HEAP8[ptr+4|0]; - HEAP8[tempDoublePtr+5|0] = HEAP8[ptr+5|0]; - HEAP8[tempDoublePtr+6|0] = HEAP8[ptr+6|0]; - HEAP8[tempDoublePtr+7|0] = HEAP8[ptr+7|0]; + return ret|0; +} +function stackSave() { + return STACKTOP|0; +} +function stackRestore(top) { + top = top|0; + STACKTOP = top; +} +function setThrew(threw, value) { + threw = threw|0; + value = value|0; + if ((__THREW__|0) == 0) { + __THREW__ = threw; + threwValue = value; } +} +function copyTempFloat(ptr) { + ptr = ptr|0; + HEAP8[tempDoublePtr] = HEAP8[ptr]; + HEAP8[tempDoublePtr+1|0] = HEAP8[ptr+1|0]; + HEAP8[tempDoublePtr+2|0] = HEAP8[ptr+2|0]; + HEAP8[tempDoublePtr+3|0] = HEAP8[ptr+3|0]; +} +function copyTempDouble(ptr) { + ptr = ptr|0; + HEAP8[tempDoublePtr] = HEAP8[ptr]; + HEAP8[tempDoublePtr+1|0] = HEAP8[ptr+1|0]; + HEAP8[tempDoublePtr+2|0] = HEAP8[ptr+2|0]; + HEAP8[tempDoublePtr+3|0] = HEAP8[ptr+3|0]; + HEAP8[tempDoublePtr+4|0] = HEAP8[ptr+4|0]; + HEAP8[tempDoublePtr+5|0] = HEAP8[ptr+5|0]; + HEAP8[tempDoublePtr+6|0] = HEAP8[ptr+6|0]; + HEAP8[tempDoublePtr+7|0] = HEAP8[ptr+7|0]; +} ''' + ''.join([''' - function setTempRet%d(value) { - value = value|0; - tempRet%d = value; - } +function setTempRet%d(value) { + value = value|0; + tempRet%d = value; +} ''' % (i, i) for i in range(10)])] + [PostSets.js + '\n'] + funcs_js + [''' %s From 5b5e5865346790df93789078d9adc95696b00b47 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 21 Aug 2013 11:49:10 -0700 Subject: [PATCH 110/112] warn on very large functions and mention OUTLINING_LIMIT --- tools/js_optimizer.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tools/js_optimizer.py b/tools/js_optimizer.py index acb87460aecec..5d7dc562023a2 100644 --- a/tools/js_optimizer.py +++ b/tools/js_optimizer.py @@ -224,6 +224,10 @@ def process(line): total_size = len(js) js = None + if 'last' in passes and len(funcs) > 0: + if max([len(func[1]) for func in funcs]) > 200000: + print >> sys.stderr, 'warning: Output contains some very large functions, consider using OUTLINING_LIMIT to break them up (see settings.js)' + # if we are making source maps, we want our debug numbering to start from the # top of the file, so avoid breaking the JS into chunks cores = 1 if source_map else int(os.environ.get('EMCC_CORES') or multiprocessing.cpu_count()) From ab506b38dbb9acb2db4f1a1b8c1608cc57409a6b Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 21 Aug 2013 15:13:43 -0700 Subject: [PATCH 111/112] only outline in asm.js mode, where it is supported --- emcc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/emcc b/emcc index df2ef38cd301c..0cd8ca2dbb9b2 100755 --- a/emcc +++ b/emcc @@ -1631,7 +1631,7 @@ try: if DEBUG: save_intermediate('closure') if js_opts: - if shared.Settings.OUTLINING_LIMIT > 0: + if shared.Settings.OUTLINING_LIMIT > 0 and shared.Settings.ASM_JS: js_optimizer_queue += ['outline'] js_optimizer_extra_info['sizeToOutline'] = shared.Settings.OUTLINING_LIMIT From d705c37e34ad11b01ad33e7c985818a14623dff7 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 16 Aug 2013 17:02:43 -0700 Subject: [PATCH 112/112] emit switches in relooper --- src/jsifier.js | 13 +- src/relooper/Relooper.cpp | 84 +- src/relooper/Relooper.h | 5 +- src/relooper/emscripten/glue.js | 11 +- src/relooper/fuzzer.py | 29 +- src/relooper/test.cpp | 100 +-- src/relooper/test.txt | 177 +++- src/relooper/test2.c | 26 +- src/relooper/test2.txt | 20 +- src/relooper/test3.c | 14 +- src/relooper/test3.txt | 43 +- src/relooper/test4.cpp | 14 +- src/relooper/test4.txt | 35 +- src/relooper/test5.cpp | 12 +- src/relooper/test5.txt | 44 +- src/relooper/test6.cpp | 8 +- src/relooper/test6.txt | 20 +- src/relooper/test_dead.cpp | 4 +- src/relooper/test_debug.cpp | 8 +- src/relooper/test_debug.txt | 20 +- src/relooper/test_fuzz1.cpp | 18 +- src/relooper/test_fuzz1.txt | 50 +- src/relooper/test_fuzz2.cpp | 8 +- src/relooper/test_fuzz2.txt | 19 +- src/relooper/test_fuzz3.cpp | 10 +- src/relooper/test_fuzz3.txt | 16 + src/relooper/test_fuzz4.cpp | 10 +- src/relooper/test_fuzz4.txt | 28 +- src/relooper/test_fuzz5.cpp | 20 +- src/relooper/test_fuzz5.txt | 64 +- src/relooper/test_fuzz6.cpp | 184 ++-- src/relooper/test_fuzz6.txt | 219 ++++- src/relooper/test_inf.cpp | 368 ++++---- src/relooper/test_inf.txt | 1410 +++++++++++++++++++++++-------- tools/shared.py | 2 +- 35 files changed, 2234 insertions(+), 879 deletions(-) diff --git a/src/jsifier.js b/src/jsifier.js index 683b1874bee3e..31e066abbb2c3 100644 --- a/src/jsifier.js +++ b/src/jsifier.js @@ -795,7 +795,13 @@ function JSify(data, functionsOnly, givenFunctions) { var label = block.labels[i]; var content = getLabelLines(label, '', true); //printErr(func.ident + ' : ' + label.ident + ' : ' + content + '\n'); - blockMap[label.ident] = Relooper.addBlock(content); + var last = label.lines[label.lines.length-1]; + if (last.intertype != 'switch') { + blockMap[label.ident] = Relooper.addBlock(content); + } else { + assert(last.signedIdent); + blockMap[label.ident] = Relooper.addBlock(content, last.signedIdent); + } } // add branchings function relevant(x) { return x && x.length > 2 ? x : 0 } // ignores ';' which valueJS and label*JS can be if empty @@ -1125,7 +1131,7 @@ function JSify(data, functionsOnly, givenFunctions) { } }); makeFuncLineActor('switch', function(item) { - var useIfs = RELOOP || item.switchLabels.length < 1024; // with a huge number of cases, if-else which looks nested to js parsers can cause problems + var useIfs = false; var phiSets = calcPhiSets(item); // Consolidate checks that go to the same label. This is important because it makes the relooper simpler and faster. var targetLabels = {}; // for each target label, the list of values going to it @@ -1139,7 +1145,8 @@ function JSify(data, functionsOnly, givenFunctions) { }); var ret = ''; var first = true; - var signedIdent = makeSignOp(item.ident, item.type, 're'); // we need to standardize for purpose of comparison + signedIdent = makeSignOp(item.ident, item.type, 're'); // we need to standardize for purpose of comparison + if (!useIfs) item.signedIdent = signedIdent; if (RELOOP) { item.groupedLabels = []; } diff --git a/src/relooper/Relooper.cpp b/src/relooper/Relooper.cpp index a457914b091ab..aefcad930bd61 100644 --- a/src/relooper/Relooper.cpp +++ b/src/relooper/Relooper.cpp @@ -104,12 +104,14 @@ void Branch::Render(Block *Target, bool SetLabel) { int Block::IdCounter = 1; // 0 is reserved for clearings -Block::Block(const char *CodeInit) : Parent(NULL), Id(Block::IdCounter++), IsCheckedMultipleEntry(false) { +Block::Block(const char *CodeInit, const char *BranchVarInit) : Parent(NULL), Id(Block::IdCounter++), IsCheckedMultipleEntry(false) { Code = strdup(CodeInit); + BranchVar = BranchVarInit ? strdup(BranchVarInit) : NULL; } Block::~Block() { if (Code) free((void*)Code); + if (BranchVar) free((void*)BranchVar); for (BlockBranchMap::iterator iter = ProcessedBranchesOut.begin(); iter != ProcessedBranchesOut.end(); iter++) { delete iter->second; } @@ -189,8 +191,16 @@ void Block::Render(bool InLoop) { } assert(DefaultTarget); // Since each block *must* branch somewhere, this must be set + if (ProcessedBranchesOut.size() > 2) assert(BranchVar); // must have a branch variable if multiple conditions + + bool useSwitch = BranchVar != NULL; + + if (useSwitch) { + PrintIndented("switch (%s) {\n", BranchVar); + } + ministring RemainingConditions; - bool First = true; + bool First = !useSwitch; // when using a switch, there is no special first for (BlockBranchMap::iterator iter = ProcessedBranchesOut.begin();; iter++) { Block *Target; Branch *Details; @@ -208,26 +218,39 @@ void Block::Render(bool InLoop) { bool HasContent = SetCurrLabel || Details->Type != Branch::Direct || HasFusedContent || Details->Code; if (iter != ProcessedBranchesOut.end()) { // If there is nothing to show in this branch, omit the condition - if (HasContent) { - PrintIndented("%sif (%s) {\n", First ? "" : "} else ", Details->Condition); - First = false; + if (useSwitch) { + PrintIndented("%s {\n", Details->Condition); } else { - if (RemainingConditions.size() > 0) RemainingConditions += " && "; - RemainingConditions += "!("; - RemainingConditions += Details->Condition; - RemainingConditions += ")"; + if (HasContent) { + PrintIndented("%sif (%s) {\n", First ? "" : "} else ", Details->Condition); + First = false; + } else { + if (RemainingConditions.size() > 0) RemainingConditions += " && "; + RemainingConditions += "!("; + if (BranchVar) { + RemainingConditions += BranchVar; + RemainingConditions += " == "; + } + RemainingConditions += Details->Condition; + RemainingConditions += ")"; + } } } else { - if (HasContent) { - if (RemainingConditions.size() > 0) { - if (First) { - PrintIndented("if (%s) {\n", RemainingConditions.c_str()); - First = false; - } else { - PrintIndented("} else if (%s) {\n", RemainingConditions.c_str()); + // this is the default + if (useSwitch) { + PrintIndented("default: {\n"); + } else { + if (HasContent) { + if (RemainingConditions.size() > 0) { + if (First) { + PrintIndented("if (%s) {\n", RemainingConditions.c_str()); + First = false; + } else { + PrintIndented("} else if (%s) {\n", RemainingConditions.c_str()); + } + } else if (!First) { + PrintIndented("} else {\n"); } - } else if (!First) { - PrintIndented("} else {\n"); } } } @@ -236,7 +259,13 @@ void Block::Render(bool InLoop) { if (HasFusedContent) { Fused->InnerMap.find(Target)->second->Render(InLoop); } + if (useSwitch && iter != ProcessedBranchesOut.end()) { + PrintIndented("break;\n"); + } if (!First) Indenter::Unindent(); + if (useSwitch) { + PrintIndented("}\n"); + } if (iter == ProcessedBranchesOut.end()) break; } if (!First) PrintIndented("}\n"); @@ -392,7 +421,7 @@ void Relooper::Calculate(Block *Entry) { PrintDebug("Splitting block %d\n", Original->Id); for (BlockSet::iterator iter = Original->BranchesIn.begin(); iter != Original->BranchesIn.end(); iter++) { Block *Prior = *iter; - Block *Split = new Block(Original->Code); + Block *Split = new Block(Original->Code, Original->BranchVar); Parent->Blocks.push_back(Split); PrintDebug(" to %d\n", Split->Id); Split->BranchesIn.insert(Prior); @@ -975,6 +1004,8 @@ void Relooper::Calculate(Block *Entry) { Root = Next; Next = NULL; SHAPE_SWITCH(Root, { + if (Simple->Inner->BranchVar) LastLoop = NULL; // a switch clears out the loop (TODO: only for breaks, not continue) + // If there is a next block, we already know at Simple creation time to make direct branches, // and we can do nothing more. If there is no next however, then Natural is where we will // go to by doing nothing, so we can potentially optimize some branches to direct. @@ -1028,6 +1059,11 @@ void Relooper::Calculate(Block *Entry) { // If we are fusing a Multiple with a loop into this Simple, then visit it now if (Fused && Fused->NeedLoop) { LoopStack.push(Fused); + } + if (Simple->Inner->BranchVar) { + LoopStack.push(NULL); // a switch means breaks are now useless, push a dummy + } + if (Fused) { RECURSE_Multiple(Fused, FindLabeledLoops); } for (BlockBranchMap::iterator iter = Simple->Inner->ProcessedBranchesOut.begin(); iter != Simple->Inner->ProcessedBranchesOut.end(); iter++) { @@ -1038,14 +1074,18 @@ void Relooper::Calculate(Block *Entry) { if (Details->Ancestor != LoopStack.top() && Details->Labeled) { LabeledShape *Labeled = Shape::IsLabeled(Details->Ancestor); Labeled->Labeled = true; - Details->Labeled = true; } else { Details->Labeled = false; } } } + if (Simple->Inner->BranchVar) { + LoopStack.pop(); + } if (Fused && Fused->NeedLoop) { LoopStack.pop(); + } + if (Fused) { Next = Fused->Next; } else { Next = Root->Next; @@ -1173,8 +1213,8 @@ void rl_set_asm_js_mode(int on) { Relooper::SetAsmJSMode(on); } -void *rl_new_block(const char *text) { - Block *ret = new Block(text); +void *rl_new_block(const char *text, const char *branch_var) { + Block *ret = new Block(text, branch_var); #if DEBUG printf(" void *b%d = rl_new_block(\"// code %d\");\n", ret->Id, ret->Id); __blockDebugMap__[ret] = ret->Id; diff --git a/src/relooper/Relooper.h b/src/relooper/Relooper.h index e54b578cdfdf3..f3dedf8c0ec45 100644 --- a/src/relooper/Relooper.h +++ b/src/relooper/Relooper.h @@ -59,9 +59,10 @@ struct Block { Shape *Parent; // The shape we are directly inside int Id; // A unique identifier const char *Code; // The string representation of the code in this block. Owning pointer (we copy the input) + const char *BranchVar; // If we have more than one branch out, the variable whose value determines where we go bool IsCheckedMultipleEntry; // If true, we are a multiple entry, so reaching us requires setting the label variable - Block(const char *CodeInit); + Block(const char *CodeInit, const char *BranchVarInit); ~Block(); void AddBranchTo(Block *Target, const char *Condition, const char *Code=NULL); @@ -235,7 +236,7 @@ extern "C" { RELOOPERDLL_API void rl_set_output_buffer(char *buffer, int size); RELOOPERDLL_API void rl_make_output_buffer(int size); RELOOPERDLL_API void rl_set_asm_js_mode(int on); -RELOOPERDLL_API void *rl_new_block(const char *text); +RELOOPERDLL_API void *rl_new_block(const char *text, const char *branch_var); RELOOPERDLL_API void rl_delete_block(void *block); RELOOPERDLL_API void rl_block_add_branch_to(void *from, void *to, const char *condition, const char *code); RELOOPERDLL_API void *rl_new_relooper(); diff --git a/src/relooper/emscripten/glue.js b/src/relooper/emscripten/glue.js index 36922185baea6..40ddabec6f35a 100644 --- a/src/relooper/emscripten/glue.js +++ b/src/relooper/emscripten/glue.js @@ -6,15 +6,22 @@ var TBUFFER_SIZE = 10*1024*1024; var tbuffer = _malloc(TBUFFER_SIZE); + var VBUFFER_SIZE = 256; + var vbuffer = _malloc(VBUFFER_SIZE); + var RelooperGlue = {}; RelooperGlue['init'] = function() { this.r = _rl_new_relooper(); }, - RelooperGlue['addBlock'] = function(text) { + RelooperGlue['addBlock'] = function(text, branchVar) { assert(this.r); assert(text.length+1 < TBUFFER_SIZE); writeStringToMemory(text, tbuffer); - var b = _rl_new_block(tbuffer); + if (branchVar) { + assert(branchVar.length+1 < VBUFFER_SIZE); + writeStringToMemory(branchVar, vbuffer); + } + var b = _rl_new_block(tbuffer, branchVar ? vbuffer : 0); _rl_relooper_add_block(this.r, b); return b; }; diff --git a/src/relooper/fuzzer.py b/src/relooper/fuzzer.py index 5f6bae3dc7379..50846d1032c55 100644 --- a/src/relooper/fuzzer.py +++ b/src/relooper/fuzzer.py @@ -26,13 +26,13 @@ pass # parts - entry = '''print('entry'); var label; var state; var decisions = %s; var index = 0; function check() { if (index == decisions.length) throw 'HALT'; return decisions[index++] }''' % str(decisions) + entry = '''print('entry'); var label; var state; var modded; var decisions = %s; var index = 0; function check() { if (index == decisions.length) throw 'HALT'; return decisions[index++] }''' % str(decisions) slow = entry + '\n' for i in range(len(branches[0])): if i > 0: slow += 'else ' b = branches[0] - slow += 'if (state %% %d == %d) { label = %d; }\n' % (len(b)+1, i, b[i]) # TODO: split range 1-n into these options + slow += 'if (modded == %d) { label = %d; }\n' % (i, b[i]) # TODO: split range 1-n into these options if len(branches[0]): slow += 'else ' slow += 'label = %d;\n' % defaults[0] @@ -51,26 +51,35 @@ ''' for i in range(1, num): - slow += ' case %d: print(%d); state = check(); \n' % (i, i) + slow += ' case %d: print(%d); state = check(); modded = state %% %d\n' % (i, i, len(branches[i])+1) b = branches[i] for j in range(len(b)): - slow += ' if (state %% %d == %d) { label = %d; break }\n' % (len(b)+1, j, b[j]) # TODO: split range 1-n into these options + slow += ' if (modded == %d) { label = %d; break }\n' % (j, b[j]) # TODO: split range 1-n into these options slow += ' label = %d; break\n' % defaults[i] + branch_vars = [] for i in range(num): + branch_var = '"modded"' if len(branches[i]) > 0 and not (len(branches[i]) == 1 and random.random() < 0.5) else 'NULL' + branch_vars.append(branch_var) + if i == 0: fast += ''' - Block *b%d = new Block("%s"); -''' % (i, entry) + Block *b%d = new Block("%s", %s); +''' % (i, entry, branch_var) else: - fast += ''' Block *b%d = new Block("print(%d); state = check();%s"); -''' % (i, i, '// ' + ('.' * int(random.expovariate(0.5/num)))) + fast += ''' Block *b%d = new Block("print(%d); state = check(); modded = state %% %d; %s", %s); +''' % (i, i, len(branches[i])+1, '// ' + ('.' * int(random.expovariate(0.5/num))), branch_var) for i in range(num): + branch_var = branch_vars[i] b = branches[i] for j in range(len(b)): - fast += ''' b%d->AddBranchTo(b%d, "state %% %d == %d"); -''' % (i, b[j], len(b)+1, j) + if branch_var == 'NULL': + fast += ''' b%d->AddBranchTo(b%d, "modded == %d"); +''' % (i, b[j], j) + else: + fast += ''' b%d->AddBranchTo(b%d, "case %d:"); +''' % (i, b[j], j) fast += ''' b%d->AddBranchTo(b%d, NULL); ''' % (i, defaults[i]) diff --git a/src/relooper/test.cpp b/src/relooper/test.cpp index b2d500d7b5aba..fbd9c7aac6c69 100644 --- a/src/relooper/test.cpp +++ b/src/relooper/test.cpp @@ -7,11 +7,11 @@ int main() { if (1) { Relooper::SetOutputBuffer(buffer, sizeof(buffer)); - printf("\n\n-- If pattern --\n\n"); + printf("\n\n-- If pattern --\n\n", "the_var"); - Block *b_a = new Block("// block A\n"); - Block *b_b = new Block("// block B\n"); - Block *b_c = new Block("// block C\n"); + Block *b_a = new Block("// block A\n", "the_var"); + Block *b_b = new Block("// block B\n", "the_var"); + Block *b_c = new Block("// block C\n", "the_var"); b_a->AddBranchTo(b_b, "check == 10", "atob();"); b_a->AddBranchTo(b_c, NULL, "atoc();"); @@ -24,7 +24,7 @@ int main() { r.AddBlock(b_c); r.Calculate(b_a); - printf("\n\n"); + printf("\n\n", "the_var"); r.Render(); puts(buffer); @@ -33,12 +33,12 @@ int main() { if (1) { Relooper::SetOutputBuffer(buffer, sizeof(buffer)); - printf("\n\n-- If-else pattern --\n\n"); + printf("\n\n-- If-else pattern --\n\n", "the_var"); - Block *b_a = new Block("// block A\n"); - Block *b_b = new Block("// block B\n"); - Block *b_c = new Block("// block C\n"); - Block *b_d = new Block("// block D\n"); + Block *b_a = new Block("// block A\n", "the_var"); + Block *b_b = new Block("// block B\n", "the_var"); + Block *b_c = new Block("// block C\n", "the_var"); + Block *b_d = new Block("// block D\n", "the_var"); b_a->AddBranchTo(b_b, "check == 15"); b_a->AddBranchTo(b_c, NULL); @@ -54,7 +54,7 @@ int main() { r.AddBlock(b_d); r.Calculate(b_a); - printf("\n\n"); + printf("\n\n", "the_var"); r.Render(); puts(buffer); @@ -63,11 +63,11 @@ int main() { if (1) { Relooper::SetOutputBuffer(buffer, sizeof(buffer)); - printf("\n\n-- Loop + tail pattern --\n\n"); + printf("\n\n-- Loop + tail pattern --\n\n", "the_var"); - Block *b_a = new Block("// block A\nvar check = maybe();\n"); - Block *b_b = new Block("// block B\n"); - Block *b_c = new Block("// block C\n"); + Block *b_a = new Block("// block A\nvar check = maybe();\n", "the_var"); + Block *b_b = new Block("// block B\n", "the_var"); + Block *b_c = new Block("// block C\n", "the_var"); b_a->AddBranchTo(b_b, NULL); @@ -80,7 +80,7 @@ int main() { r.AddBlock(b_c); r.Calculate(b_a); - printf("\n\n"); + printf("\n\n", "the_var"); r.Render(); puts(buffer); @@ -89,29 +89,29 @@ int main() { if (1) { Relooper::SetOutputBuffer(buffer, sizeof(buffer)); - printf("\n\n-- Loop with phi to head \n\n"); + printf("\n\n-- Loop with phi to head \n\n", "the_var"); void *block_map[10000]; void *rl = rl_new_relooper(); - void *b1 = rl_new_block("// code 1"); + void *b1 = rl_new_block("// code 1", "the_var"); block_map[1] = b1; rl_relooper_add_block(rl, block_map[1]); - void *b2 = rl_new_block("// code 2"); + void *b2 = rl_new_block("// code 2", "the_var"); block_map[2] = b2; rl_relooper_add_block(rl, block_map[2]); - void *b3 = rl_new_block("// code 3"); + void *b3 = rl_new_block("// code 3", "the_var"); block_map[3] = b3; rl_relooper_add_block(rl, block_map[3]); - void *b4 = rl_new_block("// code 4"); + void *b4 = rl_new_block("// code 4", "the_var"); block_map[4] = b4; rl_relooper_add_block(rl, block_map[4]); - void *b5 = rl_new_block("// code 5"); + void *b5 = rl_new_block("// code 5", "the_var"); block_map[5] = b5; rl_relooper_add_block(rl, block_map[5]); - void *b6 = rl_new_block("// code 6"); + void *b6 = rl_new_block("// code 6", "the_var"); block_map[6] = b6; rl_relooper_add_block(rl, block_map[6]); - void *b7 = rl_new_block("// code 7"); + void *b7 = rl_new_block("// code 7", "the_var"); block_map[7] = b7; rl_relooper_add_block(rl, block_map[7]); rl_block_add_branch_to(block_map[1], block_map[2], NULL, "var $i_0 = 0;var $x_0 = 5; "); @@ -132,13 +132,13 @@ int main() { if (1) { Relooper::SetOutputBuffer(buffer, sizeof(buffer)); - printf("\n\n-- phi on split dead ends --\n\n"); + printf("\n\n-- phi on split dead ends --\n\n", "the_var"); - Block *b_a = new Block("// block A...................................................................................................\n"); - Block *b_b = new Block("// block B...................................................................................................\n"); - Block *b_c = new Block("// block C...................................................................................................\n"); - Block *b_d = new Block("// block D\n"); // small and splittable! - Block *b_e = new Block("// block E\n"); + Block *b_a = new Block("// block A...................................................................................................\n", "the_var"); + Block *b_b = new Block("// block B...................................................................................................\n", "the_var"); + Block *b_c = new Block("// block C...................................................................................................\n", "the_var"); + Block *b_d = new Block("// block D\n", "the_var"); // small and splittable! + Block *b_e = new Block("// block E\n", "the_var"); b_a->AddBranchTo(b_b, "chak()", "atob();"); b_a->AddBranchTo(b_c, NULL, "atoc();"); @@ -155,7 +155,7 @@ int main() { r.AddBlock(b_e); r.Calculate(b_a); - printf("\n\n"); + printf("\n\n", "the_var"); r.Render(); puts(buffer); @@ -164,12 +164,12 @@ int main() { if (1) { Relooper::SetOutputBuffer(buffer, sizeof(buffer)); - printf("\n\n-- Unbalanced with a dead end --\n\n"); + printf("\n\n-- Unbalanced with a dead end --\n\n", "the_var"); - Block *b_a = new Block("// block A\n"); - Block *b_b = new Block("// block B\n"); - Block *b_c = new Block("return C;\n"); - Block *b_d = new Block("// block D\n"); + Block *b_a = new Block("// block A\n", "the_var"); + Block *b_b = new Block("// block B\n", "the_var"); + Block *b_c = new Block("return C;\n", "the_var"); + Block *b_d = new Block("// block D\n", "the_var"); b_a->AddBranchTo(b_b, "check == 10"); b_a->AddBranchTo(b_c, NULL); // c is a dead end @@ -185,7 +185,7 @@ int main() { r.AddBlock(b_d); r.Calculate(b_a); - printf("\n\n"); + printf("\n\n", "the_var"); r.Render(); puts(buffer); @@ -194,14 +194,14 @@ int main() { if (1) { Relooper::SetOutputBuffer(buffer, sizeof(buffer)); - printf("\n\n-- if (expensive || expensive2) X else Y; Z --\n\n"); + printf("\n\n-- if (expensive || expensive2) X else Y; Z --\n\n", "the_var"); - Block *b_a = new Block("// block A\n"); - Block *b_b = new Block("// block B\n"); - Block *b_c = new Block("// block C;\n"); - Block *b_d = new Block("// block D\n"); - Block *b_e = new Block("// block E\n"); - Block *b_f = new Block("// block F\n"); + Block *b_a = new Block("// block A\n", "the_var"); + Block *b_b = new Block("// block B\n", "the_var"); + Block *b_c = new Block("// block C;\n", "the_var"); + Block *b_d = new Block("// block D\n", "the_var"); + Block *b_e = new Block("// block E\n", "the_var"); + Block *b_f = new Block("// block F\n", "the_var"); b_a->AddBranchTo(b_c, "expensive()"); b_a->AddBranchTo(b_b, NULL); @@ -226,7 +226,7 @@ int main() { r.AddBlock(b_f); r.Calculate(b_a); - printf("\n\n"); + printf("\n\n", "the_var"); r.Render(); puts(buffer); @@ -235,11 +235,11 @@ int main() { if (1) { Relooper::SetOutputBuffer(buffer, sizeof(buffer)); - printf("\n\n-- conditional loop --\n\n"); + printf("\n\n-- conditional loop --\n\n", "the_var"); - Block *b_a = new Block("// block A\n"); - Block *b_b = new Block("// block B\n"); - Block *b_c = new Block("// block C\n"); + Block *b_a = new Block("// block A\n", "the_var"); + Block *b_b = new Block("// block B\n", "the_var"); + Block *b_c = new Block("// block C\n", "the_var"); b_a->AddBranchTo(b_b, "shouldLoop()"); b_a->AddBranchTo(b_c, NULL); @@ -253,7 +253,7 @@ int main() { r.AddBlock(b_c); r.Calculate(b_a); - printf("\n\n"); + printf("\n\n", "the_var"); r.Render(); puts(buffer); diff --git a/src/relooper/test.txt b/src/relooper/test.txt index 6c9108465c700..2c530567eae76 100644 --- a/src/relooper/test.txt +++ b/src/relooper/test.txt @@ -5,13 +5,21 @@ // block A -if (check == 10) { +switch (the_var) { +check == 10 { atob(); // block B - btoc(); -} else { + switch (the_var) { + default: { + btoc(); + } + } + break; +} +default: { atoc(); } +} // block C @@ -21,10 +29,22 @@ if (check == 10) { // block A -if (check == 15) { +switch (the_var) { +check == 15 { // block B -} else { + switch (the_var) { + default: { + } + } + break; +} +default: { // block C + switch (the_var) { + default: { + } + } +} } // block D @@ -34,13 +54,22 @@ if (check == 15) { -while(1) { +L9: while(1) { // block A var check = maybe(); + switch (the_var) { + default: { + } + } // block B - if (!(check == 41)) { + switch (the_var) { + check == 41 { break; } + default: { + break L9; + } + } } // block C @@ -49,30 +78,56 @@ while(1) { -- Loop with phi to head // code 1 -var $i_0 = 0;var $x_0 = 5; -while(1) { +switch (the_var) { +default: { + var $i_0 = 0;var $x_0 = 5; +} +} +L14: while(1) { // code 2 - if (!($2)) { + switch (the_var) { + $2 { + break; + } + default: { var $x_1 = $x_0; label = 18; - break; + break L14; + } } // code 3 - if ($6) { + switch (the_var) { + $6 { + break L14; break; - } else { + } + default: { var $i_0 = $7;var $x_0 = $5; } + } } if (label == 18) { // code 7 } // code 4 -if ($10) { +switch (the_var) { +$10 { // code 5 + switch (the_var) { + default: { + } + } + break; +} +default: { +} } // code 6 -var $x_1 = $13; +switch (the_var) { +default: { + var $x_1 = $13; +} +} // code 7 @@ -82,17 +137,29 @@ var $x_1 = $13; // block A................................................................................................... -if (chak()) { +switch (the_var) { +chak() { atob(); // block B................................................................................................... - btod(); + switch (the_var) { + default: { + btod(); + } + } // block D -} else { + break; +} +default: { atoc(); // block C................................................................................................... - ctod2(); + switch (the_var) { + default: { + ctod2(); + } + } // block D } +} @@ -101,12 +168,25 @@ if (chak()) { // block A -if (!(check == 10)) { +switch (the_var) { +check == 10 { + break; +} +default: { return C; } +} while(1) { // block B + switch (the_var) { + default: { + } + } // block D + switch (the_var) { + default: { + } + } } @@ -116,24 +196,49 @@ while(1) { // block A -do { - if (expensive()) { +L37: do { + switch (the_var) { + expensive() { label = 33; - } else { + break; + } + default: { // block B - if (expensive2()) { + switch (the_var) { + expensive2() { label = 33; + break L37; break; } + default: { + } + } // block D + switch (the_var) { + default: { + } + } + } } } while(0); if (label == 33) { // block C; + switch (the_var) { + default: { + } + } } while(1) { // block E + switch (the_var) { + default: { + } + } // block F + switch (the_var) { + default: { + } + } } @@ -143,13 +248,25 @@ while(1) { // block A -if (shouldLoop()) { - while(1) { - // block B - if (!(moarLoop())) { - break; +L46: do { + switch (the_var) { + shouldLoop() { + while(1) { + // block B + switch (the_var) { + moarLoop() { + break; + } + default: { + break L46; + } + } } + break; } -} + default: { + } + } +} while(0); // block C diff --git a/src/relooper/test2.c b/src/relooper/test2.c index 118a2627f60cd..156945c3dbcd8 100644 --- a/src/relooper/test2.c +++ b/src/relooper/test2.c @@ -6,20 +6,20 @@ int main() { rl_set_output_buffer(buffer, sizeof(buffer)); void *r = rl_new_relooper(); - void *ep = rl_new_block("ep"); + void *ep = rl_new_block("ep", "var"); rl_relooper_add_block(r, ep); - void *LBB1 = rl_new_block("LBB1"); + void *LBB1 = rl_new_block("LBB1", "the_var"); rl_relooper_add_block(r, LBB1); - void *LBB2 = rl_new_block("LBB2"); + void *LBB2 = rl_new_block("LBB2", "the_var"); rl_relooper_add_block(r, LBB2); - void *LBB3 = rl_new_block("LBB3"); + void *LBB3 = rl_new_block("LBB3", "the_var"); rl_relooper_add_block(r, LBB3); /* - void *LBB4 = rl_new_block("LBB4"); + void *LBB4 = rl_new_block("LBB4", "the_var"); rl_relooper_add_block(r, LBB4); - void *LBB5 = rl_new_block("LBB5"); + void *LBB5 = rl_new_block("LBB5", "the_var"); rl_relooper_add_block(r, LBB5); - void *LBB6 = rl_new_block("LBB6"); + void *LBB6 = rl_new_block("LBB6", "the_var"); rl_relooper_add_block(r, LBB6); */ rl_block_add_branch_to(ep, LBB1, "ep -> LBB1", NULL); @@ -27,13 +27,13 @@ int main() { rl_block_add_branch_to(LBB1, LBB2, "LBB1 -> LBB2", NULL); rl_block_add_branch_to(LBB1, LBB3, NULL, NULL); rl_block_add_branch_to(LBB2, LBB3, NULL, NULL); -// rl_block_add_branch_to(LBB3, LBB4, "LBB3 -> LBB4"); -// rl_block_add_branch_to(LBB3, LBB6, "LBB3 -> LBB6"); +// rl_block_add_branch_to(LBB3, LBB4, "LBB3 -> LBB4", "the_var"); +// rl_block_add_branch_to(LBB3, LBB6, "LBB3 -> LBB6", "the_var"); /* - rl_block_add_branch_to(LBB4, LBB5, "LBB4 -> LBB5"); - rl_block_add_branch_to(LBB4, LBB6, "LBB4 -> LBB6"); - rl_block_add_branch_to(LBB5, LBB6, "LBB5 -> LBB6"); - rl_block_add_branch_to(LBB5, LBB5, "LBB5 -> LBB5"); + rl_block_add_branch_to(LBB4, LBB5, "LBB4 -> LBB5", "the_var"); + rl_block_add_branch_to(LBB4, LBB6, "LBB4 -> LBB6", "the_var"); + rl_block_add_branch_to(LBB5, LBB6, "LBB5 -> LBB6", "the_var"); + rl_block_add_branch_to(LBB5, LBB5, "LBB5 -> LBB5", "the_var"); */ rl_relooper_calculate(r, ep); rl_relooper_render(r); diff --git a/src/relooper/test2.txt b/src/relooper/test2.txt index 2f3e5ca11efbc..a558a8b79787c 100644 --- a/src/relooper/test2.txt +++ b/src/relooper/test2.txt @@ -1,11 +1,25 @@ ep -do { - if (ep -> LBB1) { +L1: do { + switch (var) { + ep -> LBB1 { LBB1 - if (!(LBB1 -> LBB2)) { + switch (the_var) { + LBB1 -> LBB2 { break; } + default: { + break L1; + } + } LBB2 + switch (the_var) { + default: { + } + } + break; + } + default: { + } } } while(0); LBB3 diff --git a/src/relooper/test3.c b/src/relooper/test3.c index 2cef14fbaaa7a..f652a2bc9a5d7 100644 --- a/src/relooper/test3.c +++ b/src/relooper/test3.c @@ -6,19 +6,19 @@ int main() { rl_set_output_buffer(buffer, sizeof(buffer)); void *r = rl_new_relooper(); - void *ep = rl_new_block("ep"); + void *ep = rl_new_block("ep", "the_var"); rl_relooper_add_block(r, ep); - void *LBB1 = rl_new_block("LBB1"); + void *LBB1 = rl_new_block("LBB1", "the_var"); rl_relooper_add_block(r, LBB1); - void *LBB2 = rl_new_block("LBB2"); + void *LBB2 = rl_new_block("LBB2", "the_var"); rl_relooper_add_block(r, LBB2); - void *LBB3 = rl_new_block("LBB3"); + void *LBB3 = rl_new_block("LBB3", "the_var"); rl_relooper_add_block(r, LBB3); - void *LBB4 = rl_new_block("LBB4"); + void *LBB4 = rl_new_block("LBB4", "the_var"); rl_relooper_add_block(r, LBB4); - void *LBB5 = rl_new_block("LBB5"); + void *LBB5 = rl_new_block("LBB5", "the_var"); rl_relooper_add_block(r, LBB5); - void *LBB6 = rl_new_block("LBB6"); + void *LBB6 = rl_new_block("LBB6", "the_var"); rl_relooper_add_block(r, LBB6); rl_block_add_branch_to(ep, LBB1, "ep -> LBB1", NULL); diff --git a/src/relooper/test3.txt b/src/relooper/test3.txt index 51199f72a05b8..f77e2618b8b6f 100644 --- a/src/relooper/test3.txt +++ b/src/relooper/test3.txt @@ -1,26 +1,55 @@ ep -do { - if (ep -> LBB1) { +L1: do { + switch (the_var) { + ep -> LBB1 { LBB1 - if (!(LBB1 -> LBB2)) { + switch (the_var) { + LBB1 -> LBB2 { break; } + default: { + break L1; + } + } LBB2 + switch (the_var) { + default: { + } + } + break; + } + default: { + } } } while(0); LBB3 -do { - if (LBB3 -> LBB4) { +L5: do { + switch (the_var) { + LBB3 -> LBB4 { LBB4 - if (!(LBB4 -> LBB5)) { + switch (the_var) { + LBB4 -> LBB5 { break; } + default: { + break L5; + } + } while(1) { LBB5 - if (LBB5 -> LBB6) { + switch (the_var) { + LBB5 -> LBB6 { + break L5; break; } + default: { + } + } } + break; + } + default: { + } } } while(0); LBB6 diff --git a/src/relooper/test4.cpp b/src/relooper/test4.cpp index 76fc8ec066580..8cbc024c1557e 100644 --- a/src/relooper/test4.cpp +++ b/src/relooper/test4.cpp @@ -7,19 +7,19 @@ int main() { void *r = rl_new_relooper(); - void *b19 = rl_new_block("//19"); + void *b19 = rl_new_block("//19", "the_var"); rl_relooper_add_block(r, b19); - void *b20 = rl_new_block("//20"); + void *b20 = rl_new_block("//20", "the_var"); rl_relooper_add_block(r, b20); - void *b21 = rl_new_block("//21"); + void *b21 = rl_new_block("//21", "the_var"); rl_relooper_add_block(r, b21); - void *b22 = rl_new_block("//22"); + void *b22 = rl_new_block("//22", "the_var"); rl_relooper_add_block(r, b22); - void *b23 = rl_new_block("//23"); + void *b23 = rl_new_block("//23", "the_var"); rl_relooper_add_block(r, b23); - void *b24 = rl_new_block("//24"); + void *b24 = rl_new_block("//24", "the_var"); rl_relooper_add_block(r, b24); - void *b28 = rl_new_block("//28"); + void *b28 = rl_new_block("//28", "the_var"); rl_relooper_add_block(r, b28); rl_block_add_branch_to(b19, b20, " 1 ", NULL); diff --git a/src/relooper/test4.txt b/src/relooper/test4.txt index ab7051c1faa29..1829e5234d8f0 100644 --- a/src/relooper/test4.txt +++ b/src/relooper/test4.txt @@ -1,23 +1,44 @@ //19 -do { - if ( 1 ) { +L1: do { + switch (the_var) { + 1 { //20 - if (!( 1 )) { - label = 4; + switch (the_var) { + 1 { break; } + default: { + label = 4; + break L1; + } + } //21 - } else { + switch (the_var) { + default: { + } + } + break; + } + default: { label = 4; } + } } while(0); if (label == 4) { //22 + switch (the_var) { + default: { + } + } } //23 -if ( 1 ) { +switch (the_var) { + 1 { //24 -} else { + break; +} +default: { //28 } +} diff --git a/src/relooper/test5.cpp b/src/relooper/test5.cpp index f86da2b3cd68d..d04d157d8b04d 100644 --- a/src/relooper/test5.cpp +++ b/src/relooper/test5.cpp @@ -7,17 +7,17 @@ int main() { void *r = rl_new_relooper(); - void *b0 = rl_new_block("//0"); + void *b0 = rl_new_block("//0", "the_var"); rl_relooper_add_block(r, b0); - void *b1 = rl_new_block("//1"); + void *b1 = rl_new_block("//1", "the_var"); rl_relooper_add_block(r, b1); - void *b2 = rl_new_block("//2"); + void *b2 = rl_new_block("//2", "the_var"); rl_relooper_add_block(r, b2); - void *b3 = rl_new_block("//3"); + void *b3 = rl_new_block("//3", "the_var"); rl_relooper_add_block(r, b3); - void *b4 = rl_new_block("//4"); + void *b4 = rl_new_block("//4", "the_var"); rl_relooper_add_block(r, b4); - void *b5 = rl_new_block("//5"); + void *b5 = rl_new_block("//5", "the_var"); rl_relooper_add_block(r, b5); rl_block_add_branch_to(b0, b1, "check(0)", NULL); diff --git a/src/relooper/test5.txt b/src/relooper/test5.txt index 83755289a42bf..82ef5edfd6f5e 100644 --- a/src/relooper/test5.txt +++ b/src/relooper/test5.txt @@ -1,32 +1,56 @@ //0 -if (check(0)) { - while(1) { +switch (the_var) { +check(0) { + L2: while(1) { //1 - if (!(check(1))) { + switch (the_var) { + check(1) { break; } + default: { + break L2; + } + } } - while(1) { + L4: while(1) { //2 - if (!(check(2))) { + switch (the_var) { + check(2) { break; } + default: { + break L4; + } + } } //3 -} else { + break; +} +default: { goingFrom0to4(); - while(1) { + L7: while(1) { //4 - if (!(check(4))) { + switch (the_var) { + check(4) { break; } + default: { + break L7; + } + } } - while(1) { + L9: while(1) { //5 - if (check(5)) { + switch (the_var) { + check(5) { + break L9; break; } + default: { + } + } } //3 } +} diff --git a/src/relooper/test6.cpp b/src/relooper/test6.cpp index 90453d4c70b95..c292e79c0e832 100644 --- a/src/relooper/test6.cpp +++ b/src/relooper/test6.cpp @@ -7,13 +7,13 @@ int main() { void *r = rl_new_relooper(); - void *b0 = rl_new_block("//0"); + void *b0 = rl_new_block("//0", "the_var"); rl_relooper_add_block(r, b0); - void *b1 = rl_new_block("//1"); + void *b1 = rl_new_block("//1", "the_var"); rl_relooper_add_block(r, b1); - void *b2 = rl_new_block("//2"); + void *b2 = rl_new_block("//2", "the_var"); rl_relooper_add_block(r, b2); - void *b3 = rl_new_block("//3"); + void *b3 = rl_new_block("//3", "the_var"); rl_relooper_add_block(r, b3); rl_block_add_branch_to(b0, b1, "check(0)", NULL); diff --git a/src/relooper/test6.txt b/src/relooper/test6.txt index 4f29f292a504a..f9d6e93aea95c 100644 --- a/src/relooper/test6.txt +++ b/src/relooper/test6.txt @@ -1,11 +1,25 @@ //0 -do { - if (check(0)) { +L1: do { + switch (the_var) { + check(0) { //1 - if (!(check(1))) { + switch (the_var) { + check(1) { break; } + default: { + break L1; + } + } //2 + switch (the_var) { + default: { + } + } + break; + } + default: { + } } } while(0); //3 diff --git a/src/relooper/test_dead.cpp b/src/relooper/test_dead.cpp index 887d254c6859f..5c9fde1bd5e3e 100644 --- a/src/relooper/test_dead.cpp +++ b/src/relooper/test_dead.cpp @@ -8,8 +8,8 @@ int main() { printf("\n\n-- If pattern --\n\n"); - Block *b_a = new Block("// block A\n"); - Block *b_b = new Block("// block B\n"); // never reached + Block *b_a = new Block("// block A\n", "the_var"); + Block *b_b = new Block("// block B\n", "waka"); // never reached b_b->AddBranchTo(b_b, NULL); diff --git a/src/relooper/test_debug.cpp b/src/relooper/test_debug.cpp index 14511b625cbab..91b6c9d7e5d1d 100644 --- a/src/relooper/test_debug.cpp +++ b/src/relooper/test_debug.cpp @@ -6,13 +6,13 @@ int main() { rl_set_output_buffer(buffer, sizeof(buffer)); void *r = rl_new_relooper(); - void *ep = rl_new_block("ep"); + void *ep = rl_new_block("ep", "the_var"); rl_relooper_add_block(r, ep); - void *LBB1 = rl_new_block("LBB1"); + void *LBB1 = rl_new_block("LBB1", "the_var"); rl_relooper_add_block(r, LBB1); - void *LBB2 = rl_new_block("LBB2"); + void *LBB2 = rl_new_block("LBB2", "the_var"); rl_relooper_add_block(r, LBB2); - void *LBB3 = rl_new_block("LBB3"); + void *LBB3 = rl_new_block("LBB3", "the_var"); rl_relooper_add_block(r, LBB3); rl_block_add_branch_to(ep, LBB1, "ep -> LBB1", NULL); diff --git a/src/relooper/test_debug.txt b/src/relooper/test_debug.txt index 4a42e64213e5b..eb33fdbc3724b 100644 --- a/src/relooper/test_debug.txt +++ b/src/relooper/test_debug.txt @@ -115,13 +115,27 @@ int main() { // === Optimizing shapes === // Fusing Multiple to Simple ep -do { - if (ep -> LBB1) { +L1: do { + switch (the_var) { + ep -> LBB1 { LBB1 - if (!(LBB1 -> LBB2)) { + switch (the_var) { + LBB1 -> LBB2 { break; } + default: { + break L1; + } + } LBB2 + switch (the_var) { + default: { + } + } + break; + } + default: { + } } } while(0); LBB3 diff --git a/src/relooper/test_fuzz1.cpp b/src/relooper/test_fuzz1.cpp index 542056947221b..ef48ea93ea88b 100644 --- a/src/relooper/test_fuzz1.cpp +++ b/src/relooper/test_fuzz1.cpp @@ -8,15 +8,15 @@ int main() { char *buffer = (char*)malloc(SIZE); Relooper::SetOutputBuffer(buffer, SIZE); - Block *b0 = new Block("print('entry'); var label; var state; var decisions = [4, 1, 7, 2, 6, 6, 8]; var index = 0; function check() { if (index == decisions.length) throw 'HALT'; return decisions[index++] }"); - Block *b1 = new Block("print(1); state = check();"); - Block *b2 = new Block("print(2); state = check();"); - Block *b3 = new Block("print(3); state = check();"); - Block *b4 = new Block("print(4); state = check();"); - Block *b5 = new Block("print(5); state = check();"); - Block *b6 = new Block("print(6); state = check();"); - Block *b7 = new Block("print(7); state = check();"); - Block *b8 = new Block("print(8); state = check();"); + Block *b0 = new Block("print('entry'); var label; var state; var decisions = [4, 1, 7, 2, 6, 6, 8]; var index = 0; function check() { if (index == decisions.length) throw 'HALT'; return decisions[index++] }", "the_var"); + Block *b1 = new Block("print(1); state = check();", "the_var"); + Block *b2 = new Block("print(2); state = check();", "the_var"); + Block *b3 = new Block("print(3); state = check();", "the_var"); + Block *b4 = new Block("print(4); state = check();", "the_var"); + Block *b5 = new Block("print(5); state = check();", "the_var"); + Block *b6 = new Block("print(6); state = check();", "the_var"); + Block *b7 = new Block("print(7); state = check();", "the_var"); + Block *b8 = new Block("print(8); state = check();", "the_var"); b0->AddBranchTo(b5, NULL); b1->AddBranchTo(b3, NULL); b2->AddBranchTo(b1, NULL); diff --git a/src/relooper/test_fuzz1.txt b/src/relooper/test_fuzz1.txt index becbc0d2c5db2..d887f5b839cc9 100644 --- a/src/relooper/test_fuzz1.txt +++ b/src/relooper/test_fuzz1.txt @@ -1,32 +1,72 @@ print('entry'); var label; var state; var decisions = [4, 1, 7, 2, 6, 6, 8]; var index = 0; function check() { if (index == decisions.length) throw 'HALT'; return decisions[index++] } +switch (the_var) { +default: { +} +} print(5); state = check(); +switch (the_var) { +default: { +} +} print(6); state = check(); -if (state == 7) { +switch (the_var) { +state == 7 { print(7); state = check(); - label = 3; + switch (the_var) { + default: { + label = 3; + } + } + break; +} +default: { +} } L5: while(1) { if (label == 3) { label = 0; print(2); state = check(); + switch (the_var) { + default: { + } + } } print(1); state = check(); + switch (the_var) { + default: { + } + } while(1) { print(3); state = check(); - if (!(state == 8)) { + switch (the_var) { + state == 8 { + break; + } + default: { continue L5; } + } print(8); state = check(); - if (!(state == 4)) { + switch (the_var) { + state == 4 { + break; + } + default: { label = 3; continue L5; } + } print(4); state = check(); - if (!(state == 3)) { + switch (the_var) { + state == 3 { + break; + } + default: { continue L5; } + } } } diff --git a/src/relooper/test_fuzz2.cpp b/src/relooper/test_fuzz2.cpp index 003753406cada..8a66b113af9a4 100644 --- a/src/relooper/test_fuzz2.cpp +++ b/src/relooper/test_fuzz2.cpp @@ -8,10 +8,10 @@ int main() { char *buffer = (char*)malloc(SIZE); Relooper::SetOutputBuffer(buffer, SIZE); - Block *b0 = new Block("print('entry'); var label; var state; var decisions = [4, 1, 4, 3, 4, 1, 2, 5, 1, 3, 5, 5, 1, 5, 2, 4, 4, 3]; var index = 0; function check() { if (index == decisions.length) throw 'HALT'; return decisions[index++] }"); - Block *b1 = new Block("print(1); state = check();"); - Block *b2 = new Block("print(2); state = check();"); - Block *b3 = new Block("print(3); state = check();"); + Block *b0 = new Block("print('entry'); var label; var state; var decisions = [4, 1, 4, 3, 4, 1, 2, 5, 1, 3, 5, 5, 1, 5, 2, 4, 4, 3]; var index = 0; function check() { if (index == decisions.length) throw 'HALT'; return decisions[index++] }", "the_var"); + Block *b1 = new Block("print(1); state = check();", "the_var"); + Block *b2 = new Block("print(2); state = check();", "the_var"); + Block *b3 = new Block("print(3); state = check();", "the_var"); b0->AddBranchTo(b1, "state == 1"); b0->AddBranchTo(b3, NULL); b1->AddBranchTo(b1, NULL); diff --git a/src/relooper/test_fuzz2.txt b/src/relooper/test_fuzz2.txt index 02b2c83be98d7..69f4350c234e8 100644 --- a/src/relooper/test_fuzz2.txt +++ b/src/relooper/test_fuzz2.txt @@ -1,13 +1,30 @@ print('entry'); var label; var state; var decisions = [4, 1, 4, 3, 4, 1, 2, 5, 1, 3, 5, 5, 1, 5, 2, 4, 4, 3]; var index = 0; function check() { if (index == decisions.length) throw 'HALT'; return decisions[index++] } -if (state == 1) { +switch (the_var) { +state == 1 { while(1) { print(1); state = check(); + switch (the_var) { + default: { + } + } } + break; +} +default: { +} } while(1) { print(3); state = check(); + switch (the_var) { + default: { + } + } print(2); state = check(); + switch (the_var) { + default: { + } + } } diff --git a/src/relooper/test_fuzz3.cpp b/src/relooper/test_fuzz3.cpp index 3f39f5da23793..cb37d50f26c8b 100644 --- a/src/relooper/test_fuzz3.cpp +++ b/src/relooper/test_fuzz3.cpp @@ -8,11 +8,11 @@ int main() { char *buffer = (char*)malloc(SIZE); Relooper::SetOutputBuffer(buffer, SIZE); - Block *b0 = new Block("print('entry'); var label; var state; var decisions = [3, 3, 4, 1, 2, 1, 2, 4, 4, 4, 2, 3, 3, 1, 2]; var index = 0; function check() { if (index == decisions.length) throw 'HALT'; return decisions[index++] }"); - Block *b1 = new Block("print(1); state = check();"); - Block *b2 = new Block("print(2); state = check();"); - Block *b3 = new Block("print(3); state = check();"); - Block *b4 = new Block("print(4); state = check();"); + Block *b0 = new Block("print('entry'); var label; var state; var decisions = [3, 3, 4, 1, 2, 1, 2, 4, 4, 4, 2, 3, 3, 1, 2]; var index = 0; function check() { if (index == decisions.length) throw 'HALT'; return decisions[index++] }", "the_var"); + Block *b1 = new Block("print(1); state = check();", "the_var"); + Block *b2 = new Block("print(2); state = check();", "the_var"); + Block *b3 = new Block("print(3); state = check();", "the_var"); + Block *b4 = new Block("print(4); state = check();", "the_var"); b0->AddBranchTo(b1, NULL); b1->AddBranchTo(b3, NULL); b2->AddBranchTo(b1, NULL); diff --git a/src/relooper/test_fuzz3.txt b/src/relooper/test_fuzz3.txt index b4b1831ddbc22..398b48038cba6 100644 --- a/src/relooper/test_fuzz3.txt +++ b/src/relooper/test_fuzz3.txt @@ -1,9 +1,25 @@ print('entry'); var label; var state; var decisions = [3, 3, 4, 1, 2, 1, 2, 4, 4, 4, 2, 3, 3, 1, 2]; var index = 0; function check() { if (index == decisions.length) throw 'HALT'; return decisions[index++] } +switch (the_var) { +default: { +} +} print(1); state = check(); +switch (the_var) { +default: { +} +} print(3); state = check(); +switch (the_var) { +default: { +} +} while(1) { print(4); state = check(); + switch (the_var) { + default: { + } + } } diff --git a/src/relooper/test_fuzz4.cpp b/src/relooper/test_fuzz4.cpp index 8f9693869c4fc..8cd93984629bc 100644 --- a/src/relooper/test_fuzz4.cpp +++ b/src/relooper/test_fuzz4.cpp @@ -8,11 +8,11 @@ int main() { char *buffer = (char*)malloc(SIZE); Relooper::SetOutputBuffer(buffer, SIZE); - Block *b0 = new Block("print('entry'); var label; var state; var decisions = [2, 2, 1, 3, 2, 2, 1, 3, 2, 3, 3, 1, 3, 2, 1]; var index = 0; function check() { if (index == decisions.length) throw 'HALT'; return decisions[index++] }"); - Block *b1 = new Block("print(1); state = check();"); - Block *b2 = new Block("print(2); state = check();"); - Block *b3 = new Block("print(3); state = check();"); - Block *b4 = new Block("print(4); state = check();"); + Block *b0 = new Block("print('entry'); var label; var state; var decisions = [2, 2, 1, 3, 2, 2, 1, 3, 2, 3, 3, 1, 3, 2, 1]; var index = 0; function check() { if (index == decisions.length) throw 'HALT'; return decisions[index++] }", "the_var"); + Block *b1 = new Block("print(1); state = check();", "the_var"); + Block *b2 = new Block("print(2); state = check();", "the_var"); + Block *b3 = new Block("print(3); state = check();", "the_var"); + Block *b4 = new Block("print(4); state = check();", "the_var"); b0->AddBranchTo(b2, "state == 2"); b0->AddBranchTo(b4, NULL); b1->AddBranchTo(b1, NULL); diff --git a/src/relooper/test_fuzz4.txt b/src/relooper/test_fuzz4.txt index 766ebdb64f451..2e2f2c6f62127 100644 --- a/src/relooper/test_fuzz4.txt +++ b/src/relooper/test_fuzz4.txt @@ -1,19 +1,41 @@ print('entry'); var label; var state; var decisions = [2, 2, 1, 3, 2, 2, 1, 3, 2, 3, 3, 1, 3, 2, 1]; var index = 0; function check() { if (index == decisions.length) throw 'HALT'; return decisions[index++] } -if (state == 2) { +switch (the_var) { +state == 2 { while(1) { print(2); state = check(); + switch (the_var) { + default: { + } + } } + break; } -while(1) { +default: { +} +} +L4: while(1) { print(4); state = check(); - if (!(state == 4)) { + switch (the_var) { + state == 4 { break; } + default: { + break L4; + } + } } print(3); state = check(); +switch (the_var) { +default: { +} +} while(1) { print(1); state = check(); + switch (the_var) { + default: { + } + } } diff --git a/src/relooper/test_fuzz5.cpp b/src/relooper/test_fuzz5.cpp index f48c31eeaa070..a58e19ba6d6cd 100644 --- a/src/relooper/test_fuzz5.cpp +++ b/src/relooper/test_fuzz5.cpp @@ -8,16 +8,16 @@ int main() { char *buffer = (char*)malloc(SIZE); Relooper::SetOutputBuffer(buffer, SIZE); - Block *b0 = new Block("print('entry'); var label; var state; var decisions = [133, 98, 134, 143, 162, 187, 130, 87, 91, 49, 102, 47, 9, 132, 179, 176, 157, 25, 64, 161, 57, 107, 16, 167, 185, 45, 191, 180, 23, 131]; var index = 0; function check() { if (index == decisions.length) throw 'HALT'; return decisions[index++] }"); - Block *b1 = new Block("print(1); state = check();"); - Block *b2 = new Block("print(2); state = check();"); - Block *b3 = new Block("print(3); state = check();"); - Block *b4 = new Block("print(4); state = check();"); - Block *b5 = new Block("print(5); state = check();"); - Block *b6 = new Block("print(6); state = check();"); - Block *b7 = new Block("print(7); state = check();"); - Block *b8 = new Block("print(8); state = check();"); - Block *b9 = new Block("print(9); state = check();"); + Block *b0 = new Block("print('entry'); var label; var state; var decisions = [133, 98, 134, 143, 162, 187, 130, 87, 91, 49, 102, 47, 9, 132, 179, 176, 157, 25, 64, 161, 57, 107, 16, 167, 185, 45, 191, 180, 23, 131]; var index = 0; function check() { if (index == decisions.length) throw 'HALT'; return decisions[index++] }", "the_var"); + Block *b1 = new Block("print(1); state = check();", "the_var"); + Block *b2 = new Block("print(2); state = check();", "the_var"); + Block *b3 = new Block("print(3); state = check();", "the_var"); + Block *b4 = new Block("print(4); state = check();", "the_var"); + Block *b5 = new Block("print(5); state = check();", "the_var"); + Block *b6 = new Block("print(6); state = check();", "the_var"); + Block *b7 = new Block("print(7); state = check();", "the_var"); + Block *b8 = new Block("print(8); state = check();", "the_var"); + Block *b9 = new Block("print(9); state = check();", "the_var"); b0->AddBranchTo(b7, NULL); b1->AddBranchTo(b4, "state % 2 == 0"); b1->AddBranchTo(b6, NULL); diff --git a/src/relooper/test_fuzz5.txt b/src/relooper/test_fuzz5.txt index 0f76d1ed76b7b..f87e5b794760f 100644 --- a/src/relooper/test_fuzz5.txt +++ b/src/relooper/test_fuzz5.txt @@ -1,52 +1,86 @@ print('entry'); var label; var state; var decisions = [133, 98, 134, 143, 162, 187, 130, 87, 91, 49, 102, 47, 9, 132, 179, 176, 157, 25, 64, 161, 57, 107, 16, 167, 185, 45, 191, 180, 23, 131]; var index = 0; function check() { if (index == decisions.length) throw 'HALT'; return decisions[index++] } +switch (the_var) { +default: { +} +} L1: while(1) { print(7); state = check(); - if (state % 3 == 1) { + switch (the_var) { + state % 3 == 1 { label = 3; - } else if (state % 3 == 0) { + break; + } + state % 3 == 0 { print(8); state = check(); - if (state % 2 == 0) { + switch (the_var) { + state % 2 == 0 { label = 5; - } else { + break; + } + default: { label = 7; } - } else { + } break; } - while(1) { + default: { + break L1; + } + } + L5: while(1) { if (label == 3) { label = 0; print(2); state = check(); + switch (the_var) { + default: { + } + } print(1); state = check(); - if (state % 2 == 0) { + switch (the_var) { + state % 2 == 0 { label = 5; - continue; - } else { + continue L5; + break; + } + default: { label = 7; - continue; + continue L5; + } } } else if (label == 5) { label = 0; print(4); state = check(); - label = 3; - continue; + switch (the_var) { + default: { + label = 3; + continue L5; + } + } } else if (label == 7) { label = 0; print(6); state = check(); - if (state % 2 == 0) { + switch (the_var) { + state % 2 == 0 { continue L1; - } else { + break; + } + default: { label = 7; - continue; + continue L5; + } } } } } while(1) { print(3); state = check(); + switch (the_var) { + default: { + } + } } diff --git a/src/relooper/test_fuzz6.cpp b/src/relooper/test_fuzz6.cpp index 76a016d685b1d..bea834ae080a1 100644 --- a/src/relooper/test_fuzz6.cpp +++ b/src/relooper/test_fuzz6.cpp @@ -8,98 +8,98 @@ int main() { char *buffer = (char*)malloc(SIZE); Relooper::SetOutputBuffer(buffer, SIZE); - Block *b0 = new Block("print('entry'); var label; var state; var decisions = [759, 1223, 618, 1805, 277, 512, 204, 1545, 606, 734, 585, 447, 1670, 1031, 665, 1728, 353, 634, 1033, 13, 658, 589, 474, 854, 405, 1111, 1640, 697, 1156, 1357, 317, 618, 990, 1401, 405, 564, 497, 829, 653, 1194, 25, 322, 1178, 198, 1565, 1419, 1608, 486, 368, 606, 813, 22, 148, 141, 261, 375, 472, 964, 1106, 694, 205, 771, 44, 675, 545, 1027, 1528, 240, 1289, 564, 792, 744, 366, 668, 823, 210, 428, 1009, 1662, 1317, 1183, 681, 14, 1334, 712, 506, 224, 695, 401, 1035, 384, 486, 1519, 122, 1186, 1487, 1819, 1702, 463, 1706, 660, 1642, 847, 991, 976, 940, 867, 46, 23, 1449, 56, 1711, 634, 404, 1558, 168, 710, 1581, 1302, 870, 997, 1295, 1739, 769, 1005, 291, 1638, 1771, 842, 659, 1695, 713, 935, 802, 1173, 1572, 850, 607, 996, 55, 1576, 321, 1815, 662, 1044, 1612, 1680, 1050, 844, 553, 278, 1447, 1662, 1094, 1797, 774, 1013, 1204, 907, 340, 1172, 1460, 869, 1264, 111, 1176, 484, 845, 258, 417, 1246, 1017, 745, 189, 333, 1658, 1395, 1764, 1786, 165, 404, 847, 1429, 1574, 403, 718, 1118, 1756, 94, 56, 1498, 1696, 1355, 840, 50, 82, 371, 1087, 875, 1337, 267, 958, 1209, 1167, 1025, 1684, 184, 962, 1496, 201, 127, 372, 1, 1005, 402, 1387, 213, 1143, 1271, 167, 10, 12, 1060, 1390, 1366, 893, 747, 1005, 481, 876, 227, 514, 589, 250, 273, 1188, 1052, 719, 219, 1006, 38, 120, 1454, 489, 672, 149, 534, 1081, 1721, 586, 330, 25, 356, 1743, 1607, 336, 981, 419, 1036, 1293, 1026, 1300, 1453, 792, 22, 45, 420, 409, 1027, 1437, 1421, 795, 136, 1276, 1610, 1593]; var index = 0; function check() { if (index == decisions.length) throw 'HALT'; return decisions[index++] }"); - Block *b1 = new Block("print(1); state = check();// ..........................................................................."); - Block *b2 = new Block("print(2); state = check();// ........."); - Block *b3 = new Block("print(3); state = check();// .................................."); - Block *b4 = new Block("print(4); state = check();// ..........................................................................................................................."); - Block *b5 = new Block("print(5); state = check();// .........................................................................................................................................."); - Block *b6 = new Block("print(6); state = check();// ........."); - Block *b7 = new Block("print(7); state = check();// ............................................................................................................................................................................................."); - Block *b8 = new Block("print(8); state = check();// ...................................................................................................................................................................................................................................................."); - Block *b9 = new Block("print(9); state = check();// ..................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................."); - Block *b10 = new Block("print(10); state = check();// ..................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................."); - Block *b11 = new Block("print(11); state = check();// ........................................................"); - Block *b12 = new Block("print(12); state = check();// ................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................"); - Block *b13 = new Block("print(13); state = check();// ..................."); - Block *b14 = new Block("print(14); state = check();// ............................."); - Block *b15 = new Block("print(15); state = check();// .................................................."); - Block *b16 = new Block("print(16); state = check();// ................................................................................................................................................................................................................................................................................................................................................................"); - Block *b17 = new Block("print(17); state = check();// ................................................................"); - Block *b18 = new Block("print(18); state = check();// .............................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................."); - Block *b19 = new Block("print(19); state = check();// ......................................................................................................................................................................................................................"); - Block *b20 = new Block("print(20); state = check();// .................................................................................................................................................................."); - Block *b21 = new Block("print(21); state = check();// ..........................."); - Block *b22 = new Block("print(22); state = check();// ........................................................................................................."); - Block *b23 = new Block("print(23); state = check();// ................................................................................................................................................................................................................."); - Block *b24 = new Block("print(24); state = check();// ..........................."); - Block *b25 = new Block("print(25); state = check();// ......................................................................................................................................................"); - Block *b26 = new Block("print(26); state = check();// ........................................................................................................................................................................................................................................................................."); - Block *b27 = new Block("print(27); state = check();// ............................................................................................................................................................................."); - Block *b28 = new Block("print(28); state = check();// .............................................................................................................."); - Block *b29 = new Block("print(29); state = check();// ..............."); - Block *b30 = new Block("print(30); state = check();// ................................................................................................................................................................................................................."); - Block *b31 = new Block("print(31); state = check();// .........................................................................................................................................................................................................."); - Block *b32 = new Block("print(32); state = check();// ......................................................................................................"); - Block *b33 = new Block("print(33); state = check();// ...."); - Block *b34 = new Block("print(34); state = check();// .........................................................................................................................................."); - Block *b35 = new Block("print(35); state = check();// ................................."); - Block *b36 = new Block("print(36); state = check();// ........................."); - Block *b37 = new Block("print(37); state = check();// ................................................................................................................................"); - Block *b38 = new Block("print(38); state = check();// ............................................................................................................................................................................................................................................................................................................................................"); - Block *b39 = new Block("print(39); state = check();// ................"); - Block *b40 = new Block("print(40); state = check();// ................................................................................................................................................"); - Block *b41 = new Block("print(41); state = check();// ..................................................................................................................................."); - Block *b42 = new Block("print(42); state = check();// ....................................................."); - Block *b43 = new Block("print(43); state = check();// ........."); - Block *b44 = new Block("print(44); state = check();// ....................................................................................................................................................."); - Block *b45 = new Block("print(45); state = check();// ............................"); - Block *b46 = new Block("print(46); state = check();// ............................................................................."); - Block *b47 = new Block("print(47); state = check();// ...................................................................................................................................."); - Block *b48 = new Block("print(48); state = check();// ............"); - Block *b49 = new Block("print(49); state = check();// ............................................................................................................................................................................................................................................................................................"); - Block *b50 = new Block("print(50); state = check();// ........................................"); - Block *b51 = new Block("print(51); state = check();// ............................................................................................."); - Block *b52 = new Block("print(52); state = check();// .............................................................................."); - Block *b53 = new Block("print(53); state = check();// .............................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................."); - Block *b54 = new Block("print(54); state = check();// ....................................."); - Block *b55 = new Block("print(55); state = check();// ..........................................................................................................................................................................................................."); - Block *b56 = new Block("print(56); state = check();// ...................................................................................................................................................................................................................."); - Block *b57 = new Block("print(57); state = check();// ..........................................................................................................................................................................................................................................................................................................................."); - Block *b58 = new Block("print(58); state = check();// ......................................................................................"); - Block *b59 = new Block("print(59); state = check();// ..................................."); - Block *b60 = new Block("print(60); state = check();// ......................................................................................................................................................................................................................................"); - Block *b61 = new Block("print(61); state = check();// ........................................................................................................................................................."); - Block *b62 = new Block("print(62); state = check();// ......................................................................................."); - Block *b63 = new Block("print(63); state = check();// ....................................................................................................................................................................."); - Block *b64 = new Block("print(64); state = check();// ......................................................................................................................................................................................................................................................................."); - Block *b65 = new Block("print(65); state = check();// ........................................................."); - Block *b66 = new Block("print(66); state = check();// ..............................................................................................................."); - Block *b67 = new Block("print(67); state = check();// ....................................................................................................................................................................................................................."); - Block *b68 = new Block("print(68); state = check();// ......................................................................................................................................................................................................................................................................................................................"); - Block *b69 = new Block("print(69); state = check();// ......................................................................................................................................................"); - Block *b70 = new Block("print(70); state = check();// .........................................................................................................................."); - Block *b71 = new Block("print(71); state = check();// ..........................................................................................................................................................................................................."); - Block *b72 = new Block("print(72); state = check();// .........................................................................................................."); - Block *b73 = new Block("print(73); state = check();// ."); - Block *b74 = new Block("print(74); state = check();// .............................................."); - Block *b75 = new Block("print(75); state = check();// ............................................."); - Block *b76 = new Block("print(76); state = check();// .............................................................................................................................................................................................................................................................................................................................................................................................................................."); - Block *b77 = new Block("print(77); state = check();// ..........................................................................................................................................................................................................................................................................................."); - Block *b78 = new Block("print(78); state = check();// .........................................................................................."); - Block *b79 = new Block("print(79); state = check();// ..................................................................................................................................................................................................................................................."); - Block *b80 = new Block("print(80); state = check();// ...................................."); - Block *b81 = new Block("print(81); state = check();// ................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................"); - Block *b82 = new Block("print(82); state = check();// ...................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................."); - Block *b83 = new Block("print(83); state = check();// ........................................................................................"); - Block *b84 = new Block("print(84); state = check();// ..................."); - Block *b85 = new Block("print(85); state = check();// ........................................................................................................................................................................................................................................................................................................................................................."); - Block *b86 = new Block("print(86); state = check();// ................................................................................................................................................................................................................................................."); - Block *b87 = new Block("print(87); state = check();// ......"); - Block *b88 = new Block("print(88); state = check();// ...................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................."); - Block *b89 = new Block("print(89); state = check();// ......................................................................................................................................................................................................................................................................................................................................................"); - Block *b90 = new Block("print(90); state = check();// ..........................................................................................................................................................................................................................."); - Block *b91 = new Block("print(91); state = check();// .............................................."); + Block *b0 = new Block("print('entry'); var label; var state; var decisions = [759, 1223, 618, 1805, 277, 512, 204, 1545, 606, 734, 585, 447, 1670, 1031, 665, 1728, 353, 634, 1033, 13, 658, 589, 474, 854, 405, 1111, 1640, 697, 1156, 1357, 317, 618, 990, 1401, 405, 564, 497, 829, 653, 1194, 25, 322, 1178, 198, 1565, 1419, 1608, 486, 368, 606, 813, 22, 148, 141, 261, 375, 472, 964, 1106, 694, 205, 771, 44, 675, 545, 1027, 1528, 240, 1289, 564, 792, 744, 366, 668, 823, 210, 428, 1009, 1662, 1317, 1183, 681, 14, 1334, 712, 506, 224, 695, 401, 1035, 384, 486, 1519, 122, 1186, 1487, 1819, 1702, 463, 1706, 660, 1642, 847, 991, 976, 940, 867, 46, 23, 1449, 56, 1711, 634, 404, 1558, 168, 710, 1581, 1302, 870, 997, 1295, 1739, 769, 1005, 291, 1638, 1771, 842, 659, 1695, 713, 935, 802, 1173, 1572, 850, 607, 996, 55, 1576, 321, 1815, 662, 1044, 1612, 1680, 1050, 844, 553, 278, 1447, 1662, 1094, 1797, 774, 1013, 1204, 907, 340, 1172, 1460, 869, 1264, 111, 1176, 484, 845, 258, 417, 1246, 1017, 745, 189, 333, 1658, 1395, 1764, 1786, 165, 404, 847, 1429, 1574, 403, 718, 1118, 1756, 94, 56, 1498, 1696, 1355, 840, 50, 82, 371, 1087, 875, 1337, 267, 958, 1209, 1167, 1025, 1684, 184, 962, 1496, 201, 127, 372, 1, 1005, 402, 1387, 213, 1143, 1271, 167, 10, 12, 1060, 1390, 1366, 893, 747, 1005, 481, 876, 227, 514, 589, 250, 273, 1188, 1052, 719, 219, 1006, 38, 120, 1454, 489, 672, 149, 534, 1081, 1721, 586, 330, 25, 356, 1743, 1607, 336, 981, 419, 1036, 1293, 1026, 1300, 1453, 792, 22, 45, 420, 409, 1027, 1437, 1421, 795, 136, 1276, 1610, 1593]; var index = 0; function check() { if (index == decisions.length) throw 'HALT'; return decisions[index++] }", "the_var"); + Block *b1 = new Block("print(1); state = check();// ...........................................................................", "the_var"); + Block *b2 = new Block("print(2); state = check();// .........", "the_var"); + Block *b3 = new Block("print(3); state = check();// ..................................", "the_var"); + Block *b4 = new Block("print(4); state = check();// ...........................................................................................................................", "the_var"); + Block *b5 = new Block("print(5); state = check();// ..........................................................................................................................................", "the_var"); + Block *b6 = new Block("print(6); state = check();// .........", "the_var"); + Block *b7 = new Block("print(7); state = check();// .............................................................................................................................................................................................", "the_var"); + Block *b8 = new Block("print(8); state = check();// ....................................................................................................................................................................................................................................................", "the_var"); + Block *b9 = new Block("print(9); state = check();// ...................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................", "the_var"); + Block *b10 = new Block("print(10); state = check();// ...................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................", "the_var"); + Block *b11 = new Block("print(11); state = check();// ........................................................", "the_var"); + Block *b12 = new Block("print(12); state = check();// ................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................", "the_var"); + Block *b13 = new Block("print(13); state = check();// ...................", "the_var"); + Block *b14 = new Block("print(14); state = check();// .............................", "the_var"); + Block *b15 = new Block("print(15); state = check();// ..................................................", "the_var"); + Block *b16 = new Block("print(16); state = check();// ................................................................................................................................................................................................................................................................................................................................................................", "the_var"); + Block *b17 = new Block("print(17); state = check();// ................................................................", "the_var"); + Block *b18 = new Block("print(18); state = check();// ..............................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................", "the_var"); + Block *b19 = new Block("print(19); state = check();// ......................................................................................................................................................................................................................", "the_var"); + Block *b20 = new Block("print(20); state = check();// ..................................................................................................................................................................", "the_var"); + Block *b21 = new Block("print(21); state = check();// ...........................", "the_var"); + Block *b22 = new Block("print(22); state = check();// .........................................................................................................", "the_var"); + Block *b23 = new Block("print(23); state = check();// .................................................................................................................................................................................................................", "the_var"); + Block *b24 = new Block("print(24); state = check();// ...........................", "the_var"); + Block *b25 = new Block("print(25); state = check();// ......................................................................................................................................................", "the_var"); + Block *b26 = new Block("print(26); state = check();// .........................................................................................................................................................................................................................................................................", "the_var"); + Block *b27 = new Block("print(27); state = check();// .............................................................................................................................................................................", "the_var"); + Block *b28 = new Block("print(28); state = check();// ..............................................................................................................", "the_var"); + Block *b29 = new Block("print(29); state = check();// ...............", "the_var"); + Block *b30 = new Block("print(30); state = check();// .................................................................................................................................................................................................................", "the_var"); + Block *b31 = new Block("print(31); state = check();// ..........................................................................................................................................................................................................", "the_var"); + Block *b32 = new Block("print(32); state = check();// ......................................................................................................", "the_var"); + Block *b33 = new Block("print(33); state = check();// ....", "the_var"); + Block *b34 = new Block("print(34); state = check();// ..........................................................................................................................................", "the_var"); + Block *b35 = new Block("print(35); state = check();// .................................", "the_var"); + Block *b36 = new Block("print(36); state = check();// .........................", "the_var"); + Block *b37 = new Block("print(37); state = check();// ................................................................................................................................", "the_var"); + Block *b38 = new Block("print(38); state = check();// ............................................................................................................................................................................................................................................................................................................................................", "the_var"); + Block *b39 = new Block("print(39); state = check();// ................", "the_var"); + Block *b40 = new Block("print(40); state = check();// ................................................................................................................................................", "the_var"); + Block *b41 = new Block("print(41); state = check();// ...................................................................................................................................", "the_var"); + Block *b42 = new Block("print(42); state = check();// .....................................................", "the_var"); + Block *b43 = new Block("print(43); state = check();// .........", "the_var"); + Block *b44 = new Block("print(44); state = check();// .....................................................................................................................................................", "the_var"); + Block *b45 = new Block("print(45); state = check();// ............................", "the_var"); + Block *b46 = new Block("print(46); state = check();// .............................................................................", "the_var"); + Block *b47 = new Block("print(47); state = check();// ....................................................................................................................................", "the_var"); + Block *b48 = new Block("print(48); state = check();// ............", "the_var"); + Block *b49 = new Block("print(49); state = check();// ............................................................................................................................................................................................................................................................................................", "the_var"); + Block *b50 = new Block("print(50); state = check();// ........................................", "the_var"); + Block *b51 = new Block("print(51); state = check();// .............................................................................................", "the_var"); + Block *b52 = new Block("print(52); state = check();// ..............................................................................", "the_var"); + Block *b53 = new Block("print(53); state = check();// ..............................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................", "the_var"); + Block *b54 = new Block("print(54); state = check();// .....................................", "the_var"); + Block *b55 = new Block("print(55); state = check();// ...........................................................................................................................................................................................................", "the_var"); + Block *b56 = new Block("print(56); state = check();// ....................................................................................................................................................................................................................", "the_var"); + Block *b57 = new Block("print(57); state = check();// ...........................................................................................................................................................................................................................................................................................................................", "the_var"); + Block *b58 = new Block("print(58); state = check();// ......................................................................................", "the_var"); + Block *b59 = new Block("print(59); state = check();// ...................................", "the_var"); + Block *b60 = new Block("print(60); state = check();// ......................................................................................................................................................................................................................................", "the_var"); + Block *b61 = new Block("print(61); state = check();// .........................................................................................................................................................", "the_var"); + Block *b62 = new Block("print(62); state = check();// .......................................................................................", "the_var"); + Block *b63 = new Block("print(63); state = check();// .....................................................................................................................................................................", "the_var"); + Block *b64 = new Block("print(64); state = check();// .......................................................................................................................................................................................................................................................................", "the_var"); + Block *b65 = new Block("print(65); state = check();// .........................................................", "the_var"); + Block *b66 = new Block("print(66); state = check();// ...............................................................................................................", "the_var"); + Block *b67 = new Block("print(67); state = check();// .....................................................................................................................................................................................................................", "the_var"); + Block *b68 = new Block("print(68); state = check();// ......................................................................................................................................................................................................................................................................................................................", "the_var"); + Block *b69 = new Block("print(69); state = check();// ......................................................................................................................................................", "the_var"); + Block *b70 = new Block("print(70); state = check();// ..........................................................................................................................", "the_var"); + Block *b71 = new Block("print(71); state = check();// ...........................................................................................................................................................................................................", "the_var"); + Block *b72 = new Block("print(72); state = check();// ..........................................................................................................", "the_var"); + Block *b73 = new Block("print(73); state = check();// .", "the_var"); + Block *b74 = new Block("print(74); state = check();// ..............................................", "the_var"); + Block *b75 = new Block("print(75); state = check();// .............................................", "the_var"); + Block *b76 = new Block("print(76); state = check();// ..............................................................................................................................................................................................................................................................................................................................................................................................................................", "the_var"); + Block *b77 = new Block("print(77); state = check();// ...........................................................................................................................................................................................................................................................................................", "the_var"); + Block *b78 = new Block("print(78); state = check();// ..........................................................................................", "the_var"); + Block *b79 = new Block("print(79); state = check();// ...................................................................................................................................................................................................................................................", "the_var"); + Block *b80 = new Block("print(80); state = check();// ....................................", "the_var"); + Block *b81 = new Block("print(81); state = check();// ................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................", "the_var"); + Block *b82 = new Block("print(82); state = check();// ....................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................", "the_var"); + Block *b83 = new Block("print(83); state = check();// ........................................................................................", "the_var"); + Block *b84 = new Block("print(84); state = check();// ...................", "the_var"); + Block *b85 = new Block("print(85); state = check();// .........................................................................................................................................................................................................................................................................................................................................................", "the_var"); + Block *b86 = new Block("print(86); state = check();// .................................................................................................................................................................................................................................................", "the_var"); + Block *b87 = new Block("print(87); state = check();// ......", "the_var"); + Block *b88 = new Block("print(88); state = check();// ....................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................", "the_var"); + Block *b89 = new Block("print(89); state = check();// ......................................................................................................................................................................................................................................................................................................................................................", "the_var"); + Block *b90 = new Block("print(90); state = check();// ...........................................................................................................................................................................................................................", "the_var"); + Block *b91 = new Block("print(91); state = check();// ..............................................", "the_var"); b0->AddBranchTo(b30, NULL); b1->AddBranchTo(b66, NULL); b2->AddBranchTo(b51, NULL); diff --git a/src/relooper/test_fuzz6.txt b/src/relooper/test_fuzz6.txt index cd93958c677a4..b9c1499ae56ae 100644 --- a/src/relooper/test_fuzz6.txt +++ b/src/relooper/test_fuzz6.txt @@ -1,108 +1,291 @@ print('entry'); var label; var state; var decisions = [759, 1223, 618, 1805, 277, 512, 204, 1545, 606, 734, 585, 447, 1670, 1031, 665, 1728, 353, 634, 1033, 13, 658, 589, 474, 854, 405, 1111, 1640, 697, 1156, 1357, 317, 618, 990, 1401, 405, 564, 497, 829, 653, 1194, 25, 322, 1178, 198, 1565, 1419, 1608, 486, 368, 606, 813, 22, 148, 141, 261, 375, 472, 964, 1106, 694, 205, 771, 44, 675, 545, 1027, 1528, 240, 1289, 564, 792, 744, 366, 668, 823, 210, 428, 1009, 1662, 1317, 1183, 681, 14, 1334, 712, 506, 224, 695, 401, 1035, 384, 486, 1519, 122, 1186, 1487, 1819, 1702, 463, 1706, 660, 1642, 847, 991, 976, 940, 867, 46, 23, 1449, 56, 1711, 634, 404, 1558, 168, 710, 1581, 1302, 870, 997, 1295, 1739, 769, 1005, 291, 1638, 1771, 842, 659, 1695, 713, 935, 802, 1173, 1572, 850, 607, 996, 55, 1576, 321, 1815, 662, 1044, 1612, 1680, 1050, 844, 553, 278, 1447, 1662, 1094, 1797, 774, 1013, 1204, 907, 340, 1172, 1460, 869, 1264, 111, 1176, 484, 845, 258, 417, 1246, 1017, 745, 189, 333, 1658, 1395, 1764, 1786, 165, 404, 847, 1429, 1574, 403, 718, 1118, 1756, 94, 56, 1498, 1696, 1355, 840, 50, 82, 371, 1087, 875, 1337, 267, 958, 1209, 1167, 1025, 1684, 184, 962, 1496, 201, 127, 372, 1, 1005, 402, 1387, 213, 1143, 1271, 167, 10, 12, 1060, 1390, 1366, 893, 747, 1005, 481, 876, 227, 514, 589, 250, 273, 1188, 1052, 719, 219, 1006, 38, 120, 1454, 489, 672, 149, 534, 1081, 1721, 586, 330, 25, 356, 1743, 1607, 336, 981, 419, 1036, 1293, 1026, 1300, 1453, 792, 22, 45, 420, 409, 1027, 1437, 1421, 795, 136, 1276, 1610, 1593]; var index = 0; function check() { if (index == decisions.length) throw 'HALT'; return decisions[index++] } -while(1) { +switch (the_var) { +default: { +} +} +L1: while(1) { print(30); state = check();// ................................................................................................................................................................................................................. - if (state % 3 == 1) { - label = 67; + switch (the_var) { + state % 3 == 0 { break; - } else if (!(state % 3 == 0)) { + } + state % 3 == 1 { + label = 67; + break L1; break; } + default: { + break L1; + } + } print(58); state = check();// ...................................................................................... + switch (the_var) { + default: { + } + } } if (label == 67) { print(66); state = check();// ............................................................................................................... + switch (the_var) { + default: { + } + } } print(6); state = check();// ......... +switch (the_var) { +default: { +} +} while(1) { print(88); state = check();// .................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................... + switch (the_var) { + default: { + } + } print(70); state = check();// .......................................................................................................................... + switch (the_var) { + default: { + } + } L10: while(1) { print(47); state = check();// .................................................................................................................................... + switch (the_var) { + default: { + } + } print(28); state = check();// .............................................................................................................. - while(1) { + switch (the_var) { + default: { + } + } + L13: while(1) { print(75); state = check();// ............................................. + switch (the_var) { + default: { + } + } print(7); state = check();// ............................................................................................................................................................................................. - if (!(state % 2 == 0)) { + switch (the_var) { + state % 2 == 0 { break; } + default: { + break L13; + } + } } print(89); state = check();// ...................................................................................................................................................................................................................................................................................................................................................... + switch (the_var) { + default: { + } + } print(68); state = check();// ...................................................................................................................................................................................................................................................................................................................... + switch (the_var) { + default: { + } + } L18: while(1) { print(51); state = check();// ............................................................................................. + switch (the_var) { + default: { + } + } L20: while(1) { print(36); state = check();// ......................... - if (state % 2 == 0) { + switch (the_var) { + state % 2 == 0 { + break L20; break; } + default: { + } + } print(16); state = check();// ................................................................................................................................................................................................................................................................................................................................................................ + switch (the_var) { + default: { + } + } print(57); state = check();// ........................................................................................................................................................................................................................................................................................................................... + switch (the_var) { + default: { + } + } print(39); state = check();// ................ - if (state % 3 == 1) { + switch (the_var) { + state % 3 == 0 { + break; + } + state % 3 == 1 { label = 74; - } else if (!(state % 3 == 0)) { - label = 32; break; } - while(1) { + default: { + label = 32; + break L20; + } + } + L25: while(1) { if (label == 74) { label = 0; print(73); state = check();// . - if (state % 3 == 1) { + switch (the_var) { + state % 3 == 1 { label = 32; break L20; - } else if (state % 3 == 0) { break; } + state % 3 == 0 { + break L25; + break; + } + default: { + } + } print(43); state = check();// ......... + switch (the_var) { + default: { + } + } print(32); state = check();// ...................................................................................................... + switch (the_var) { + default: { + } + } print(83); state = check();// ........................................................................................ + switch (the_var) { + default: { + } + } print(77); state = check();// ........................................................................................................................................................................................................................................................................................... + switch (the_var) { + default: { + } + } print(76); state = check();// .............................................................................................................................................................................................................................................................................................................................................................................................................................. + switch (the_var) { + default: { + } + } print(22); state = check();// ......................................................................................................... + switch (the_var) { + default: { + } + } } print(72); state = check();// .......................................................................................................... - if (state % 2 == 0) { + switch (the_var) { + state % 2 == 0 { label = 92; break L20; + break; + } + default: { + } } print(80); state = check();// .................................... - if (state % 2 == 0) { + switch (the_var) { + state % 2 == 0 { continue L18; + break; + } + default: { + } } print(50); state = check();// ........................................ + switch (the_var) { + default: { + } + } print(29); state = check();// ............... + switch (the_var) { + default: { + } + } print(8); state = check();// .................................................................................................................................................................................................................................................... - if (state % 2 == 0) { + switch (the_var) { + state % 2 == 0 { continue L10; + break; + } + default: { + } } print(19); state = check();// ...................................................................................................................................................................................................................... + switch (the_var) { + default: { + } + } print(56); state = check();// .................................................................................................................................................................................................................... + switch (the_var) { + default: { + } + } print(34); state = check();// .......................................................................................................................................... - label = 74; + switch (the_var) { + default: { + label = 74; + } + } } print(62); state = check();// ....................................................................................... + switch (the_var) { + default: { + } + } } if (label == 32) { label = 0; print(31); state = check();// .......................................................................................................................................................................................................... + switch (the_var) { + default: { + } + } } else if (label == 92) { label = 0; print(91); state = check();// .............................................. + switch (the_var) { + default: { + } + } print(33); state = check();// .... + switch (the_var) { + default: { + } + } } print(60); state = check();// ...................................................................................................................................................................................................................................... + switch (the_var) { + default: { + } + } print(10); state = check();// ................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................... + switch (the_var) { + default: { + } + } print(52); state = check();// .............................................................................. - if (state % 2 == 0) { + switch (the_var) { + state % 2 == 0 { break L10; + break; + } + default: { + } } print(2); state = check();// ......... + switch (the_var) { + default: { + } + } } } print(61); state = check();// ......................................................................................................................................................... + switch (the_var) { + default: { + } + } } diff --git a/src/relooper/test_inf.cpp b/src/relooper/test_inf.cpp index 66e712eda9e0f..1363bf4b74fdb 100644 --- a/src/relooper/test_inf.cpp +++ b/src/relooper/test_inf.cpp @@ -4,556 +4,556 @@ int main() { void *rl = rl_new_relooper(); char buffer[100000]; rl_set_output_buffer(buffer, sizeof(buffer)); - void *b0 = rl_new_block("code 0"); + void *b0 = rl_new_block("code 0", "the_var"); block_map[0] = b0; rl_relooper_add_block(rl, block_map[0]); - void *b1 = rl_new_block("code 1"); + void *b1 = rl_new_block("code 1", "the_var"); block_map[1] = b1; rl_relooper_add_block(rl, block_map[1]); - void *b2 = rl_new_block("code 2"); + void *b2 = rl_new_block("code 2", "the_var"); block_map[2] = b2; rl_relooper_add_block(rl, block_map[2]); - void *b3 = rl_new_block("code 3"); + void *b3 = rl_new_block("code 3", "the_var"); block_map[3] = b3; rl_relooper_add_block(rl, block_map[3]); - void *b4 = rl_new_block("code 4"); + void *b4 = rl_new_block("code 4", "the_var"); block_map[4] = b4; rl_relooper_add_block(rl, block_map[4]); - void *b5 = rl_new_block("code 5"); + void *b5 = rl_new_block("code 5", "the_var"); block_map[5] = b5; rl_relooper_add_block(rl, block_map[5]); - void *b6 = rl_new_block("code 6"); + void *b6 = rl_new_block("code 6", "the_var"); block_map[6] = b6; rl_relooper_add_block(rl, block_map[6]); - void *b7 = rl_new_block("code 7"); + void *b7 = rl_new_block("code 7", "the_var"); block_map[7] = b7; rl_relooper_add_block(rl, block_map[7]); - void *b8 = rl_new_block("code 8"); + void *b8 = rl_new_block("code 8", "the_var"); block_map[8] = b8; rl_relooper_add_block(rl, block_map[8]); - void *b9 = rl_new_block("code 9"); + void *b9 = rl_new_block("code 9", "the_var"); block_map[9] = b9; rl_relooper_add_block(rl, block_map[9]); - void *b10 = rl_new_block("code 10"); + void *b10 = rl_new_block("code 10", "the_var"); block_map[10] = b10; rl_relooper_add_block(rl, block_map[10]); - void *b11 = rl_new_block("code 11"); + void *b11 = rl_new_block("code 11", "the_var"); block_map[11] = b11; rl_relooper_add_block(rl, block_map[11]); - void *b12 = rl_new_block("code 12"); + void *b12 = rl_new_block("code 12", "the_var"); block_map[12] = b12; rl_relooper_add_block(rl, block_map[12]); - void *b13 = rl_new_block("code 13"); + void *b13 = rl_new_block("code 13", "the_var"); block_map[13] = b13; rl_relooper_add_block(rl, block_map[13]); - void *b14 = rl_new_block("code 14"); + void *b14 = rl_new_block("code 14", "the_var"); block_map[14] = b14; rl_relooper_add_block(rl, block_map[14]); - void *b15 = rl_new_block("code 15"); + void *b15 = rl_new_block("code 15", "the_var"); block_map[15] = b15; rl_relooper_add_block(rl, block_map[15]); - void *b16 = rl_new_block("code 16"); + void *b16 = rl_new_block("code 16", "the_var"); block_map[16] = b16; rl_relooper_add_block(rl, block_map[16]); - void *b17 = rl_new_block("code 17"); + void *b17 = rl_new_block("code 17", "the_var"); block_map[17] = b17; rl_relooper_add_block(rl, block_map[17]); - void *b18 = rl_new_block("code 18"); + void *b18 = rl_new_block("code 18", "the_var"); block_map[18] = b18; rl_relooper_add_block(rl, block_map[18]); - void *b19 = rl_new_block("code 19"); + void *b19 = rl_new_block("code 19", "the_var"); block_map[19] = b19; rl_relooper_add_block(rl, block_map[19]); - void *b20 = rl_new_block("code 20"); + void *b20 = rl_new_block("code 20", "the_var"); block_map[20] = b20; rl_relooper_add_block(rl, block_map[20]); - void *b21 = rl_new_block("code 21"); + void *b21 = rl_new_block("code 21", "the_var"); block_map[21] = b21; rl_relooper_add_block(rl, block_map[21]); - void *b22 = rl_new_block("code 22"); + void *b22 = rl_new_block("code 22", "the_var"); block_map[22] = b22; rl_relooper_add_block(rl, block_map[22]); - void *b23 = rl_new_block("code 23"); + void *b23 = rl_new_block("code 23", "the_var"); block_map[23] = b23; rl_relooper_add_block(rl, block_map[23]); - void *b24 = rl_new_block("code 24"); + void *b24 = rl_new_block("code 24", "the_var"); block_map[24] = b24; rl_relooper_add_block(rl, block_map[24]); - void *b25 = rl_new_block("code 25"); + void *b25 = rl_new_block("code 25", "the_var"); block_map[25] = b25; rl_relooper_add_block(rl, block_map[25]); - void *b26 = rl_new_block("code 26"); + void *b26 = rl_new_block("code 26", "the_var"); block_map[26] = b26; rl_relooper_add_block(rl, block_map[26]); - void *b27 = rl_new_block("code 27"); + void *b27 = rl_new_block("code 27", "the_var"); block_map[27] = b27; rl_relooper_add_block(rl, block_map[27]); - void *b28 = rl_new_block("code 28"); + void *b28 = rl_new_block("code 28", "the_var"); block_map[28] = b28; rl_relooper_add_block(rl, block_map[28]); - void *b29 = rl_new_block("code 29"); + void *b29 = rl_new_block("code 29", "the_var"); block_map[29] = b29; rl_relooper_add_block(rl, block_map[29]); - void *b30 = rl_new_block("code 30"); + void *b30 = rl_new_block("code 30", "the_var"); block_map[30] = b30; rl_relooper_add_block(rl, block_map[30]); - void *b31 = rl_new_block("code 31"); + void *b31 = rl_new_block("code 31", "the_var"); block_map[31] = b31; rl_relooper_add_block(rl, block_map[31]); - void *b32 = rl_new_block("code 32"); + void *b32 = rl_new_block("code 32", "the_var"); block_map[32] = b32; rl_relooper_add_block(rl, block_map[32]); - void *b33 = rl_new_block("code 33"); + void *b33 = rl_new_block("code 33", "the_var"); block_map[33] = b33; rl_relooper_add_block(rl, block_map[33]); - void *b34 = rl_new_block("code 34"); + void *b34 = rl_new_block("code 34", "the_var"); block_map[34] = b34; rl_relooper_add_block(rl, block_map[34]); - void *b35 = rl_new_block("code 35"); + void *b35 = rl_new_block("code 35", "the_var"); block_map[35] = b35; rl_relooper_add_block(rl, block_map[35]); - void *b36 = rl_new_block("code 36"); + void *b36 = rl_new_block("code 36", "the_var"); block_map[36] = b36; rl_relooper_add_block(rl, block_map[36]); - void *b37 = rl_new_block("code 37"); + void *b37 = rl_new_block("code 37", "the_var"); block_map[37] = b37; rl_relooper_add_block(rl, block_map[37]); - void *b38 = rl_new_block("code 38"); + void *b38 = rl_new_block("code 38", "the_var"); block_map[38] = b38; rl_relooper_add_block(rl, block_map[38]); - void *b39 = rl_new_block("code 39"); + void *b39 = rl_new_block("code 39", "the_var"); block_map[39] = b39; rl_relooper_add_block(rl, block_map[39]); - void *b40 = rl_new_block("code 40"); + void *b40 = rl_new_block("code 40", "the_var"); block_map[40] = b40; rl_relooper_add_block(rl, block_map[40]); - void *b41 = rl_new_block("code 41"); + void *b41 = rl_new_block("code 41", "the_var"); block_map[41] = b41; rl_relooper_add_block(rl, block_map[41]); - void *b42 = rl_new_block("code 42"); + void *b42 = rl_new_block("code 42", "the_var"); block_map[42] = b42; rl_relooper_add_block(rl, block_map[42]); - void *b43 = rl_new_block("code 43"); + void *b43 = rl_new_block("code 43", "the_var"); block_map[43] = b43; rl_relooper_add_block(rl, block_map[43]); - void *b44 = rl_new_block("code 44"); + void *b44 = rl_new_block("code 44", "the_var"); block_map[44] = b44; rl_relooper_add_block(rl, block_map[44]); - void *b45 = rl_new_block("code 45"); + void *b45 = rl_new_block("code 45", "the_var"); block_map[45] = b45; rl_relooper_add_block(rl, block_map[45]); - void *b46 = rl_new_block("code 46"); + void *b46 = rl_new_block("code 46", "the_var"); block_map[46] = b46; rl_relooper_add_block(rl, block_map[46]); - void *b47 = rl_new_block("code 47"); + void *b47 = rl_new_block("code 47", "the_var"); block_map[47] = b47; rl_relooper_add_block(rl, block_map[47]); - void *b48 = rl_new_block("code 48"); + void *b48 = rl_new_block("code 48", "the_var"); block_map[48] = b48; rl_relooper_add_block(rl, block_map[48]); - void *b49 = rl_new_block("code 49"); + void *b49 = rl_new_block("code 49", "the_var"); block_map[49] = b49; rl_relooper_add_block(rl, block_map[49]); - void *b50 = rl_new_block("code 50"); + void *b50 = rl_new_block("code 50", "the_var"); block_map[50] = b50; rl_relooper_add_block(rl, block_map[50]); - void *b51 = rl_new_block("code 51"); + void *b51 = rl_new_block("code 51", "the_var"); block_map[51] = b51; rl_relooper_add_block(rl, block_map[51]); - void *b52 = rl_new_block("code 52"); + void *b52 = rl_new_block("code 52", "the_var"); block_map[52] = b52; rl_relooper_add_block(rl, block_map[52]); - void *b53 = rl_new_block("code 53"); + void *b53 = rl_new_block("code 53", "the_var"); block_map[53] = b53; rl_relooper_add_block(rl, block_map[53]); - void *b54 = rl_new_block("code 54"); + void *b54 = rl_new_block("code 54", "the_var"); block_map[54] = b54; rl_relooper_add_block(rl, block_map[54]); - void *b55 = rl_new_block("code 55"); + void *b55 = rl_new_block("code 55", "the_var"); block_map[55] = b55; rl_relooper_add_block(rl, block_map[55]); - void *b56 = rl_new_block("code 56"); + void *b56 = rl_new_block("code 56", "the_var"); block_map[56] = b56; rl_relooper_add_block(rl, block_map[56]); - void *b57 = rl_new_block("code 57"); + void *b57 = rl_new_block("code 57", "the_var"); block_map[57] = b57; rl_relooper_add_block(rl, block_map[57]); - void *b58 = rl_new_block("code 58"); + void *b58 = rl_new_block("code 58", "the_var"); block_map[58] = b58; rl_relooper_add_block(rl, block_map[58]); - void *b59 = rl_new_block("code 59"); + void *b59 = rl_new_block("code 59", "the_var"); block_map[59] = b59; rl_relooper_add_block(rl, block_map[59]); - void *b60 = rl_new_block("code 60"); + void *b60 = rl_new_block("code 60", "the_var"); block_map[60] = b60; rl_relooper_add_block(rl, block_map[60]); - void *b61 = rl_new_block("code 61"); + void *b61 = rl_new_block("code 61", "the_var"); block_map[61] = b61; rl_relooper_add_block(rl, block_map[61]); - void *b62 = rl_new_block("code 62"); + void *b62 = rl_new_block("code 62", "the_var"); block_map[62] = b62; rl_relooper_add_block(rl, block_map[62]); - void *b63 = rl_new_block("code 63"); + void *b63 = rl_new_block("code 63", "the_var"); block_map[63] = b63; rl_relooper_add_block(rl, block_map[63]); - void *b64 = rl_new_block("code 64"); + void *b64 = rl_new_block("code 64", "the_var"); block_map[64] = b64; rl_relooper_add_block(rl, block_map[64]); - void *b65 = rl_new_block("code 65"); + void *b65 = rl_new_block("code 65", "the_var"); block_map[65] = b65; rl_relooper_add_block(rl, block_map[65]); - void *b66 = rl_new_block("code 66"); + void *b66 = rl_new_block("code 66", "the_var"); block_map[66] = b66; rl_relooper_add_block(rl, block_map[66]); - void *b67 = rl_new_block("code 67"); + void *b67 = rl_new_block("code 67", "the_var"); block_map[67] = b67; rl_relooper_add_block(rl, block_map[67]); - void *b68 = rl_new_block("code 68"); + void *b68 = rl_new_block("code 68", "the_var"); block_map[68] = b68; rl_relooper_add_block(rl, block_map[68]); - void *b69 = rl_new_block("code 69"); + void *b69 = rl_new_block("code 69", "the_var"); block_map[69] = b69; rl_relooper_add_block(rl, block_map[69]); - void *b70 = rl_new_block("code 70"); + void *b70 = rl_new_block("code 70", "the_var"); block_map[70] = b70; rl_relooper_add_block(rl, block_map[70]); - void *b71 = rl_new_block("code 71"); + void *b71 = rl_new_block("code 71", "the_var"); block_map[71] = b71; rl_relooper_add_block(rl, block_map[71]); - void *b72 = rl_new_block("code 72"); + void *b72 = rl_new_block("code 72", "the_var"); block_map[72] = b72; rl_relooper_add_block(rl, block_map[72]); - void *b73 = rl_new_block("code 73"); + void *b73 = rl_new_block("code 73", "the_var"); block_map[73] = b73; rl_relooper_add_block(rl, block_map[73]); - void *b74 = rl_new_block("code 74"); + void *b74 = rl_new_block("code 74", "the_var"); block_map[74] = b74; rl_relooper_add_block(rl, block_map[74]); - void *b75 = rl_new_block("code 75"); + void *b75 = rl_new_block("code 75", "the_var"); block_map[75] = b75; rl_relooper_add_block(rl, block_map[75]); - void *b76 = rl_new_block("code 76"); + void *b76 = rl_new_block("code 76", "the_var"); block_map[76] = b76; rl_relooper_add_block(rl, block_map[76]); - void *b77 = rl_new_block("code 77"); + void *b77 = rl_new_block("code 77", "the_var"); block_map[77] = b77; rl_relooper_add_block(rl, block_map[77]); - void *b78 = rl_new_block("code 78"); + void *b78 = rl_new_block("code 78", "the_var"); block_map[78] = b78; rl_relooper_add_block(rl, block_map[78]); - void *b79 = rl_new_block("code 79"); + void *b79 = rl_new_block("code 79", "the_var"); block_map[79] = b79; rl_relooper_add_block(rl, block_map[79]); - void *b80 = rl_new_block("code 80"); + void *b80 = rl_new_block("code 80", "the_var"); block_map[80] = b80; rl_relooper_add_block(rl, block_map[80]); - void *b81 = rl_new_block("code 81"); + void *b81 = rl_new_block("code 81", "the_var"); block_map[81] = b81; rl_relooper_add_block(rl, block_map[81]); - void *b82 = rl_new_block("code 82"); + void *b82 = rl_new_block("code 82", "the_var"); block_map[82] = b82; rl_relooper_add_block(rl, block_map[82]); - void *b83 = rl_new_block("code 83"); + void *b83 = rl_new_block("code 83", "the_var"); block_map[83] = b83; rl_relooper_add_block(rl, block_map[83]); - void *b84 = rl_new_block("code 84"); + void *b84 = rl_new_block("code 84", "the_var"); block_map[84] = b84; rl_relooper_add_block(rl, block_map[84]); - void *b85 = rl_new_block("code 85"); + void *b85 = rl_new_block("code 85", "the_var"); block_map[85] = b85; rl_relooper_add_block(rl, block_map[85]); - void *b86 = rl_new_block("code 86"); + void *b86 = rl_new_block("code 86", "the_var"); block_map[86] = b86; rl_relooper_add_block(rl, block_map[86]); - void *b87 = rl_new_block("code 87"); + void *b87 = rl_new_block("code 87", "the_var"); block_map[87] = b87; rl_relooper_add_block(rl, block_map[87]); - void *b88 = rl_new_block("code 88"); + void *b88 = rl_new_block("code 88", "the_var"); block_map[88] = b88; rl_relooper_add_block(rl, block_map[88]); - void *b89 = rl_new_block("code 89"); + void *b89 = rl_new_block("code 89", "the_var"); block_map[89] = b89; rl_relooper_add_block(rl, block_map[89]); - void *b90 = rl_new_block("code 90"); + void *b90 = rl_new_block("code 90", "the_var"); block_map[90] = b90; rl_relooper_add_block(rl, block_map[90]); - void *b91 = rl_new_block("code 91"); + void *b91 = rl_new_block("code 91", "the_var"); block_map[91] = b91; rl_relooper_add_block(rl, block_map[91]); - void *b92 = rl_new_block("code 92"); + void *b92 = rl_new_block("code 92", "the_var"); block_map[92] = b92; rl_relooper_add_block(rl, block_map[92]); - void *b93 = rl_new_block("code 93"); + void *b93 = rl_new_block("code 93", "the_var"); block_map[93] = b93; rl_relooper_add_block(rl, block_map[93]); - void *b94 = rl_new_block("code 94"); + void *b94 = rl_new_block("code 94", "the_var"); block_map[94] = b94; rl_relooper_add_block(rl, block_map[94]); - void *b95 = rl_new_block("code 95"); + void *b95 = rl_new_block("code 95", "the_var"); block_map[95] = b95; rl_relooper_add_block(rl, block_map[95]); - void *b96 = rl_new_block("code 96"); + void *b96 = rl_new_block("code 96", "the_var"); block_map[96] = b96; rl_relooper_add_block(rl, block_map[96]); - void *b97 = rl_new_block("code 97"); + void *b97 = rl_new_block("code 97", "the_var"); block_map[97] = b97; rl_relooper_add_block(rl, block_map[97]); - void *b98 = rl_new_block("code 98"); + void *b98 = rl_new_block("code 98", "the_var"); block_map[98] = b98; rl_relooper_add_block(rl, block_map[98]); - void *b99 = rl_new_block("code 99"); + void *b99 = rl_new_block("code 99", "the_var"); block_map[99] = b99; rl_relooper_add_block(rl, block_map[99]); - void *b100 = rl_new_block("code 100"); + void *b100 = rl_new_block("code 100", "the_var"); block_map[100] = b100; rl_relooper_add_block(rl, block_map[100]); - void *b101 = rl_new_block("code 101"); + void *b101 = rl_new_block("code 101", "the_var"); block_map[101] = b101; rl_relooper_add_block(rl, block_map[101]); - void *b102 = rl_new_block("code 102"); + void *b102 = rl_new_block("code 102", "the_var"); block_map[102] = b102; rl_relooper_add_block(rl, block_map[102]); - void *b103 = rl_new_block("code 103"); + void *b103 = rl_new_block("code 103", "the_var"); block_map[103] = b103; rl_relooper_add_block(rl, block_map[103]); - void *b104 = rl_new_block("code 104"); + void *b104 = rl_new_block("code 104", "the_var"); block_map[104] = b104; rl_relooper_add_block(rl, block_map[104]); - void *b105 = rl_new_block("code 105"); + void *b105 = rl_new_block("code 105", "the_var"); block_map[105] = b105; rl_relooper_add_block(rl, block_map[105]); - void *b106 = rl_new_block("code 106"); + void *b106 = rl_new_block("code 106", "the_var"); block_map[106] = b106; rl_relooper_add_block(rl, block_map[106]); - void *b107 = rl_new_block("code 107"); + void *b107 = rl_new_block("code 107", "the_var"); block_map[107] = b107; rl_relooper_add_block(rl, block_map[107]); - void *b108 = rl_new_block("code 108"); + void *b108 = rl_new_block("code 108", "the_var"); block_map[108] = b108; rl_relooper_add_block(rl, block_map[108]); - void *b109 = rl_new_block("code 109"); + void *b109 = rl_new_block("code 109", "the_var"); block_map[109] = b109; rl_relooper_add_block(rl, block_map[109]); - void *b110 = rl_new_block("code 110"); + void *b110 = rl_new_block("code 110", "the_var"); block_map[110] = b110; rl_relooper_add_block(rl, block_map[110]); - void *b111 = rl_new_block("code 111"); + void *b111 = rl_new_block("code 111", "the_var"); block_map[111] = b111; rl_relooper_add_block(rl, block_map[111]); - void *b112 = rl_new_block("code 112"); + void *b112 = rl_new_block("code 112", "the_var"); block_map[112] = b112; rl_relooper_add_block(rl, block_map[112]); - void *b113 = rl_new_block("code 113"); + void *b113 = rl_new_block("code 113", "the_var"); block_map[113] = b113; rl_relooper_add_block(rl, block_map[113]); - void *b114 = rl_new_block("code 114"); + void *b114 = rl_new_block("code 114", "the_var"); block_map[114] = b114; rl_relooper_add_block(rl, block_map[114]); - void *b115 = rl_new_block("code 115"); + void *b115 = rl_new_block("code 115", "the_var"); block_map[115] = b115; rl_relooper_add_block(rl, block_map[115]); - void *b116 = rl_new_block("code 116"); + void *b116 = rl_new_block("code 116", "the_var"); block_map[116] = b116; rl_relooper_add_block(rl, block_map[116]); - void *b117 = rl_new_block("code 117"); + void *b117 = rl_new_block("code 117", "the_var"); block_map[117] = b117; rl_relooper_add_block(rl, block_map[117]); - void *b118 = rl_new_block("code 118"); + void *b118 = rl_new_block("code 118", "the_var"); block_map[118] = b118; rl_relooper_add_block(rl, block_map[118]); - void *b119 = rl_new_block("code 119"); + void *b119 = rl_new_block("code 119", "the_var"); block_map[119] = b119; rl_relooper_add_block(rl, block_map[119]); - void *b120 = rl_new_block("code 120"); + void *b120 = rl_new_block("code 120", "the_var"); block_map[120] = b120; rl_relooper_add_block(rl, block_map[120]); - void *b121 = rl_new_block("code 121"); + void *b121 = rl_new_block("code 121", "the_var"); block_map[121] = b121; rl_relooper_add_block(rl, block_map[121]); - void *b122 = rl_new_block("code 122"); + void *b122 = rl_new_block("code 122", "the_var"); block_map[122] = b122; rl_relooper_add_block(rl, block_map[122]); - void *b123 = rl_new_block("code 123"); + void *b123 = rl_new_block("code 123", "the_var"); block_map[123] = b123; rl_relooper_add_block(rl, block_map[123]); - void *b124 = rl_new_block("code 124"); + void *b124 = rl_new_block("code 124", "the_var"); block_map[124] = b124; rl_relooper_add_block(rl, block_map[124]); - void *b125 = rl_new_block("code 125"); + void *b125 = rl_new_block("code 125", "the_var"); block_map[125] = b125; rl_relooper_add_block(rl, block_map[125]); - void *b126 = rl_new_block("code 126"); + void *b126 = rl_new_block("code 126", "the_var"); block_map[126] = b126; rl_relooper_add_block(rl, block_map[126]); - void *b127 = rl_new_block("code 127"); + void *b127 = rl_new_block("code 127", "the_var"); block_map[127] = b127; rl_relooper_add_block(rl, block_map[127]); - void *b128 = rl_new_block("code 128"); + void *b128 = rl_new_block("code 128", "the_var"); block_map[128] = b128; rl_relooper_add_block(rl, block_map[128]); - void *b129 = rl_new_block("code 129"); + void *b129 = rl_new_block("code 129", "the_var"); block_map[129] = b129; rl_relooper_add_block(rl, block_map[129]); - void *b130 = rl_new_block("code 130"); + void *b130 = rl_new_block("code 130", "the_var"); block_map[130] = b130; rl_relooper_add_block(rl, block_map[130]); - void *b131 = rl_new_block("code 131"); + void *b131 = rl_new_block("code 131", "the_var"); block_map[131] = b131; rl_relooper_add_block(rl, block_map[131]); - void *b132 = rl_new_block("code 132"); + void *b132 = rl_new_block("code 132", "the_var"); block_map[132] = b132; rl_relooper_add_block(rl, block_map[132]); - void *b133 = rl_new_block("code 133"); + void *b133 = rl_new_block("code 133", "the_var"); block_map[133] = b133; rl_relooper_add_block(rl, block_map[133]); - void *b134 = rl_new_block("code 134"); + void *b134 = rl_new_block("code 134", "the_var"); block_map[134] = b134; rl_relooper_add_block(rl, block_map[134]); - void *b135 = rl_new_block("code 135"); + void *b135 = rl_new_block("code 135", "the_var"); block_map[135] = b135; rl_relooper_add_block(rl, block_map[135]); - void *b136 = rl_new_block("code 136"); + void *b136 = rl_new_block("code 136", "the_var"); block_map[136] = b136; rl_relooper_add_block(rl, block_map[136]); - void *b137 = rl_new_block("code 137"); + void *b137 = rl_new_block("code 137", "the_var"); block_map[137] = b137; rl_relooper_add_block(rl, block_map[137]); - void *b138 = rl_new_block("code 138"); + void *b138 = rl_new_block("code 138", "the_var"); block_map[138] = b138; rl_relooper_add_block(rl, block_map[138]); - void *b139 = rl_new_block("code 139"); + void *b139 = rl_new_block("code 139", "the_var"); block_map[139] = b139; rl_relooper_add_block(rl, block_map[139]); - void *b140 = rl_new_block("code 140"); + void *b140 = rl_new_block("code 140", "the_var"); block_map[140] = b140; rl_relooper_add_block(rl, block_map[140]); - void *b141 = rl_new_block("code 141"); + void *b141 = rl_new_block("code 141", "the_var"); block_map[141] = b141; rl_relooper_add_block(rl, block_map[141]); - void *b142 = rl_new_block("code 142"); + void *b142 = rl_new_block("code 142", "the_var"); block_map[142] = b142; rl_relooper_add_block(rl, block_map[142]); - void *b143 = rl_new_block("code 143"); + void *b143 = rl_new_block("code 143", "the_var"); block_map[143] = b143; rl_relooper_add_block(rl, block_map[143]); - void *b144 = rl_new_block("code 144"); + void *b144 = rl_new_block("code 144", "the_var"); block_map[144] = b144; rl_relooper_add_block(rl, block_map[144]); - void *b145 = rl_new_block("code 145"); + void *b145 = rl_new_block("code 145", "the_var"); block_map[145] = b145; rl_relooper_add_block(rl, block_map[145]); - void *b146 = rl_new_block("code 146"); + void *b146 = rl_new_block("code 146", "the_var"); block_map[146] = b146; rl_relooper_add_block(rl, block_map[146]); - void *b147 = rl_new_block("code 147"); + void *b147 = rl_new_block("code 147", "the_var"); block_map[147] = b147; rl_relooper_add_block(rl, block_map[147]); - void *b148 = rl_new_block("code 148"); + void *b148 = rl_new_block("code 148", "the_var"); block_map[148] = b148; rl_relooper_add_block(rl, block_map[148]); - void *b149 = rl_new_block("code 149"); + void *b149 = rl_new_block("code 149", "the_var"); block_map[149] = b149; rl_relooper_add_block(rl, block_map[149]); - void *b150 = rl_new_block("code 150"); + void *b150 = rl_new_block("code 150", "the_var"); block_map[150] = b150; rl_relooper_add_block(rl, block_map[150]); - void *b151 = rl_new_block("code 151"); + void *b151 = rl_new_block("code 151", "the_var"); block_map[151] = b151; rl_relooper_add_block(rl, block_map[151]); - void *b152 = rl_new_block("code 152"); + void *b152 = rl_new_block("code 152", "the_var"); block_map[152] = b152; rl_relooper_add_block(rl, block_map[152]); - void *b153 = rl_new_block("code 153"); + void *b153 = rl_new_block("code 153", "the_var"); block_map[153] = b153; rl_relooper_add_block(rl, block_map[153]); - void *b154 = rl_new_block("code 154"); + void *b154 = rl_new_block("code 154", "the_var"); block_map[154] = b154; rl_relooper_add_block(rl, block_map[154]); - void *b155 = rl_new_block("code 155"); + void *b155 = rl_new_block("code 155", "the_var"); block_map[155] = b155; rl_relooper_add_block(rl, block_map[155]); - void *b156 = rl_new_block("code 156"); + void *b156 = rl_new_block("code 156", "the_var"); block_map[156] = b156; rl_relooper_add_block(rl, block_map[156]); - void *b157 = rl_new_block("code 157"); + void *b157 = rl_new_block("code 157", "the_var"); block_map[157] = b157; rl_relooper_add_block(rl, block_map[157]); - void *b158 = rl_new_block("code 158"); + void *b158 = rl_new_block("code 158", "the_var"); block_map[158] = b158; rl_relooper_add_block(rl, block_map[158]); - void *b159 = rl_new_block("code 159"); + void *b159 = rl_new_block("code 159", "the_var"); block_map[159] = b159; rl_relooper_add_block(rl, block_map[159]); - void *b160 = rl_new_block("code 160"); + void *b160 = rl_new_block("code 160", "the_var"); block_map[160] = b160; rl_relooper_add_block(rl, block_map[160]); - void *b161 = rl_new_block("code 161"); + void *b161 = rl_new_block("code 161", "the_var"); block_map[161] = b161; rl_relooper_add_block(rl, block_map[161]); - void *b162 = rl_new_block("code 162"); + void *b162 = rl_new_block("code 162", "the_var"); block_map[162] = b162; rl_relooper_add_block(rl, block_map[162]); - void *b163 = rl_new_block("code 163"); + void *b163 = rl_new_block("code 163", "the_var"); block_map[163] = b163; rl_relooper_add_block(rl, block_map[163]); - void *b164 = rl_new_block("code 164"); + void *b164 = rl_new_block("code 164", "the_var"); block_map[164] = b164; rl_relooper_add_block(rl, block_map[164]); - void *b165 = rl_new_block("code 165"); + void *b165 = rl_new_block("code 165", "the_var"); block_map[165] = b165; rl_relooper_add_block(rl, block_map[165]); - void *b166 = rl_new_block("code 166"); + void *b166 = rl_new_block("code 166", "the_var"); block_map[166] = b166; rl_relooper_add_block(rl, block_map[166]); - void *b167 = rl_new_block("code 167"); + void *b167 = rl_new_block("code 167", "the_var"); block_map[167] = b167; rl_relooper_add_block(rl, block_map[167]); - void *b168 = rl_new_block("code 168"); + void *b168 = rl_new_block("code 168", "the_var"); block_map[168] = b168; rl_relooper_add_block(rl, block_map[168]); - void *b169 = rl_new_block("code 169"); + void *b169 = rl_new_block("code 169", "the_var"); block_map[169] = b169; rl_relooper_add_block(rl, block_map[169]); - void *b170 = rl_new_block("code 170"); + void *b170 = rl_new_block("code 170", "the_var"); block_map[170] = b170; rl_relooper_add_block(rl, block_map[170]); - void *b171 = rl_new_block("code 171"); + void *b171 = rl_new_block("code 171", "the_var"); block_map[171] = b171; rl_relooper_add_block(rl, block_map[171]); - void *b172 = rl_new_block("code 172"); + void *b172 = rl_new_block("code 172", "the_var"); block_map[172] = b172; rl_relooper_add_block(rl, block_map[172]); - void *b173 = rl_new_block("code 173"); + void *b173 = rl_new_block("code 173", "the_var"); block_map[173] = b173; rl_relooper_add_block(rl, block_map[173]); - void *b174 = rl_new_block("code 174"); + void *b174 = rl_new_block("code 174", "the_var"); block_map[174] = b174; rl_relooper_add_block(rl, block_map[174]); - void *b175 = rl_new_block("code 175"); + void *b175 = rl_new_block("code 175", "the_var"); block_map[175] = b175; rl_relooper_add_block(rl, block_map[175]); - void *b176 = rl_new_block("code 176"); + void *b176 = rl_new_block("code 176", "the_var"); block_map[176] = b176; rl_relooper_add_block(rl, block_map[176]); - void *b177 = rl_new_block("code 177"); + void *b177 = rl_new_block("code 177", "the_var"); block_map[177] = b177; rl_relooper_add_block(rl, block_map[177]); - void *b178 = rl_new_block("code 178"); + void *b178 = rl_new_block("code 178", "the_var"); block_map[178] = b178; rl_relooper_add_block(rl, block_map[178]); - void *b179 = rl_new_block("code 179"); + void *b179 = rl_new_block("code 179", "the_var"); block_map[179] = b179; rl_relooper_add_block(rl, block_map[179]); - void *b180 = rl_new_block("code 180"); + void *b180 = rl_new_block("code 180", "the_var"); block_map[180] = b180; rl_relooper_add_block(rl, block_map[180]); - void *b181 = rl_new_block("code 181"); + void *b181 = rl_new_block("code 181", "the_var"); block_map[181] = b181; rl_relooper_add_block(rl, block_map[181]); - void *b182 = rl_new_block("code 182"); + void *b182 = rl_new_block("code 182", "the_var"); block_map[182] = b182; rl_relooper_add_block(rl, block_map[182]); - void *b183 = rl_new_block("code 183"); + void *b183 = rl_new_block("code 183", "the_var"); block_map[183] = b183; rl_relooper_add_block(rl, block_map[183]); rl_block_add_branch_to(block_map[0], block_map[2], "uint(i4) >= uint(i5)", NULL); diff --git a/src/relooper/test_inf.txt b/src/relooper/test_inf.txt index bc2fad3e9e1ea..6db32edbc1fe7 100644 --- a/src/relooper/test_inf.txt +++ b/src/relooper/test_inf.txt @@ -1,385 +1,1131 @@ code 0 -if (uint(i4) >= uint(i5)) { +switch (the_var) { +uint(i4) >= uint(i5) { code 2 -} else { + switch (the_var) { + default: { + } + } + break; +} +default: { code 1 + switch (the_var) { + default: { + } + } +} } code 3 -if (!(i2 == 0)) { - code 4 - while(1) { - code 5 - if (uint(i6) >= uint(i7)) { - code 7 - } else { - code 6 - } - code 8 - if (uint(i6) >= uint(i7)) { - code 10 - } else { - code 9 +L5: do { + switch (the_var) { + i2 == 0 { + break; + } + default: { + code 4 + switch (the_var) { + default: { } - code 11 - if (uint(i5) >= uint(i6)) { - code 13 - } else { - code 12 } - code 14 - if (!(i2 != 0)) { - break; + while(1) { + code 5 + switch (the_var) { + uint(i6) >= uint(i7) { + code 7 + switch (the_var) { + default: { + } + } + break; + } + default: { + code 6 + switch (the_var) { + default: { + } + } + } + } + code 8 + switch (the_var) { + uint(i6) >= uint(i7) { + code 10 + switch (the_var) { + default: { + } + } + break; + } + default: { + code 9 + switch (the_var) { + default: { + } + } + } + } + code 11 + switch (the_var) { + uint(i5) >= uint(i6) { + code 13 + switch (the_var) { + default: { + } + } + break; + } + default: { + code 12 + switch (the_var) { + default: { + } + } + } + } + code 14 + switch (the_var) { + i2 != 0 { + break; + } + default: { + break L5; + } + } } } -} + } +} while(0); code 15 -if (uint(i4) >= uint(i5)) { +switch (the_var) { +uint(i4) >= uint(i5) { code 17 -} else { + switch (the_var) { + default: { + } + } + break; +} +default: { code 16 + switch (the_var) { + default: { + } + } +} } code 18 -if (!(i2 == 0)) { - code 19 - while(1) { - code 20 - if (uint(i5) >= uint(i6)) { - code 22 - } else { - code 21 - } - code 23 - if (uint(i5) >= uint(i6)) { - code 25 - } else { - code 24 - } - code 26 - if (uint(i5) >= uint(i6)) { - code 28 - } else { - code 27 - } - code 29 - if (uint(i5) >= uint(i6)) { - code 31 - } else { - code 30 - } - code 32 - if (uint(i5) >= uint(i6)) { - code 34 - } else { - code 33 - } - code 35 - if (uint(i5) >= uint(i6)) { - code 37 - } else { - code 36 - } - code 38 - if (uint(i5) >= uint(i6)) { - code 40 - } else { - code 39 - } - code 41 - if (uint(i5) >= uint(i6)) { - code 43 - } else { - code 42 - } - code 44 - if (uint(i5) >= uint(i6)) { - code 46 - } else { - code 45 - } - code 47 - if (uint(i5) >= uint(i6)) { - code 49 - } else { - code 48 - } - code 50 - if (uint(i5) >= uint(i6)) { - code 52 - } else { - code 51 - } - code 53 - if (uint(i5) >= uint(i6)) { - code 55 - } else { - code 54 - } - code 56 - if (uint(i5) >= uint(i6)) { - code 58 - } else { - code 57 - } - code 59 - if (uint(i5) >= uint(i6)) { - code 61 - } else { - code 60 - } - code 62 - if (uint(i5) >= uint(i6)) { - code 64 - } else { - code 63 - } - code 65 - if (uint(i5) >= uint(i6)) { - code 67 - } else { - code 66 - } - code 68 - if (uint(i5) >= uint(i6)) { - code 70 - } else { - code 69 - } - code 71 - if (uint(i5) >= uint(i6)) { - code 73 - } else { - code 72 - } - code 74 - if (uint(i5) >= uint(i6)) { - code 76 - } else { - code 75 - } - code 77 - if (uint(i5) >= uint(i6)) { - code 79 - } else { - code 78 - } - code 80 - if (uint(i5) >= uint(i6)) { - code 82 - } else { - code 81 - } - code 83 - if (uint(i5) >= uint(i6)) { - code 85 - } else { - code 84 - } - code 86 - if (uint(i5) >= uint(i6)) { - code 88 - } else { - code 87 - } - code 89 - if (uint(i5) >= uint(i6)) { - code 91 - } else { - code 90 - } - code 92 - if (uint(i5) >= uint(i6)) { - code 94 - } else { - code 93 - } - code 95 - if (uint(i5) >= uint(i6)) { - code 97 - } else { - code 96 +L26: do { + switch (the_var) { + i2 == 0 { + break; + } + default: { + code 19 + switch (the_var) { + default: { } - code 98 - if (uint(i5) >= uint(i6)) { - code 100 - } else { - code 99 } - code 101 - if (!(i2 != 0)) { - break; + while(1) { + code 20 + switch (the_var) { + uint(i5) >= uint(i6) { + code 22 + switch (the_var) { + default: { + } + } + break; + } + default: { + code 21 + switch (the_var) { + default: { + } + } + } + } + code 23 + switch (the_var) { + uint(i5) >= uint(i6) { + code 25 + switch (the_var) { + default: { + } + } + break; + } + default: { + code 24 + switch (the_var) { + default: { + } + } + } + } + code 26 + switch (the_var) { + uint(i5) >= uint(i6) { + code 28 + switch (the_var) { + default: { + } + } + break; + } + default: { + code 27 + switch (the_var) { + default: { + } + } + } + } + code 29 + switch (the_var) { + uint(i5) >= uint(i6) { + code 31 + switch (the_var) { + default: { + } + } + break; + } + default: { + code 30 + switch (the_var) { + default: { + } + } + } + } + code 32 + switch (the_var) { + uint(i5) >= uint(i6) { + code 34 + switch (the_var) { + default: { + } + } + break; + } + default: { + code 33 + switch (the_var) { + default: { + } + } + } + } + code 35 + switch (the_var) { + uint(i5) >= uint(i6) { + code 37 + switch (the_var) { + default: { + } + } + break; + } + default: { + code 36 + switch (the_var) { + default: { + } + } + } + } + code 38 + switch (the_var) { + uint(i5) >= uint(i6) { + code 40 + switch (the_var) { + default: { + } + } + break; + } + default: { + code 39 + switch (the_var) { + default: { + } + } + } + } + code 41 + switch (the_var) { + uint(i5) >= uint(i6) { + code 43 + switch (the_var) { + default: { + } + } + break; + } + default: { + code 42 + switch (the_var) { + default: { + } + } + } + } + code 44 + switch (the_var) { + uint(i5) >= uint(i6) { + code 46 + switch (the_var) { + default: { + } + } + break; + } + default: { + code 45 + switch (the_var) { + default: { + } + } + } + } + code 47 + switch (the_var) { + uint(i5) >= uint(i6) { + code 49 + switch (the_var) { + default: { + } + } + break; + } + default: { + code 48 + switch (the_var) { + default: { + } + } + } + } + code 50 + switch (the_var) { + uint(i5) >= uint(i6) { + code 52 + switch (the_var) { + default: { + } + } + break; + } + default: { + code 51 + switch (the_var) { + default: { + } + } + } + } + code 53 + switch (the_var) { + uint(i5) >= uint(i6) { + code 55 + switch (the_var) { + default: { + } + } + break; + } + default: { + code 54 + switch (the_var) { + default: { + } + } + } + } + code 56 + switch (the_var) { + uint(i5) >= uint(i6) { + code 58 + switch (the_var) { + default: { + } + } + break; + } + default: { + code 57 + switch (the_var) { + default: { + } + } + } + } + code 59 + switch (the_var) { + uint(i5) >= uint(i6) { + code 61 + switch (the_var) { + default: { + } + } + break; + } + default: { + code 60 + switch (the_var) { + default: { + } + } + } + } + code 62 + switch (the_var) { + uint(i5) >= uint(i6) { + code 64 + switch (the_var) { + default: { + } + } + break; + } + default: { + code 63 + switch (the_var) { + default: { + } + } + } + } + code 65 + switch (the_var) { + uint(i5) >= uint(i6) { + code 67 + switch (the_var) { + default: { + } + } + break; + } + default: { + code 66 + switch (the_var) { + default: { + } + } + } + } + code 68 + switch (the_var) { + uint(i5) >= uint(i6) { + code 70 + switch (the_var) { + default: { + } + } + break; + } + default: { + code 69 + switch (the_var) { + default: { + } + } + } + } + code 71 + switch (the_var) { + uint(i5) >= uint(i6) { + code 73 + switch (the_var) { + default: { + } + } + break; + } + default: { + code 72 + switch (the_var) { + default: { + } + } + } + } + code 74 + switch (the_var) { + uint(i5) >= uint(i6) { + code 76 + switch (the_var) { + default: { + } + } + break; + } + default: { + code 75 + switch (the_var) { + default: { + } + } + } + } + code 77 + switch (the_var) { + uint(i5) >= uint(i6) { + code 79 + switch (the_var) { + default: { + } + } + break; + } + default: { + code 78 + switch (the_var) { + default: { + } + } + } + } + code 80 + switch (the_var) { + uint(i5) >= uint(i6) { + code 82 + switch (the_var) { + default: { + } + } + break; + } + default: { + code 81 + switch (the_var) { + default: { + } + } + } + } + code 83 + switch (the_var) { + uint(i5) >= uint(i6) { + code 85 + switch (the_var) { + default: { + } + } + break; + } + default: { + code 84 + switch (the_var) { + default: { + } + } + } + } + code 86 + switch (the_var) { + uint(i5) >= uint(i6) { + code 88 + switch (the_var) { + default: { + } + } + break; + } + default: { + code 87 + switch (the_var) { + default: { + } + } + } + } + code 89 + switch (the_var) { + uint(i5) >= uint(i6) { + code 91 + switch (the_var) { + default: { + } + } + break; + } + default: { + code 90 + switch (the_var) { + default: { + } + } + } + } + code 92 + switch (the_var) { + uint(i5) >= uint(i6) { + code 94 + switch (the_var) { + default: { + } + } + break; + } + default: { + code 93 + switch (the_var) { + default: { + } + } + } + } + code 95 + switch (the_var) { + uint(i5) >= uint(i6) { + code 97 + switch (the_var) { + default: { + } + } + break; + } + default: { + code 96 + switch (the_var) { + default: { + } + } + } + } + code 98 + switch (the_var) { + uint(i5) >= uint(i6) { + code 100 + switch (the_var) { + default: { + } + } + break; + } + default: { + code 99 + switch (the_var) { + default: { + } + } + } + } + code 101 + switch (the_var) { + i2 != 0 { + break; + } + default: { + break L26; + } + } } } -} + } +} while(0); code 102 -if (uint(i4) >= uint(i5)) { +switch (the_var) { +uint(i4) >= uint(i5) { code 104 -} else { + switch (the_var) { + default: { + } + } + break; +} +default: { code 103 + switch (the_var) { + default: { + } + } +} } code 105 -if (!(i2 == 0)) { - code 106 - while(1) { - code 107 - if (uint(i5) >= uint(i6)) { - code 109 - } else { - code 108 - } - code 110 - if (uint(i5) >= uint(i6)) { - code 112 - } else { - code 111 - } - code 113 - if (uint(i5) >= uint(i6)) { - code 115 - } else { - code 114 - } - code 116 - if (uint(i5) >= uint(i6)) { - code 118 - } else { - code 117 - } - code 119 - if (uint(i5) >= uint(i6)) { - code 121 - } else { - code 120 - } - code 122 - if (uint(i5) >= uint(i6)) { - code 124 - } else { - code 123 - } - code 125 - if (uint(i5) >= uint(i6)) { - code 127 - } else { - code 126 - } - code 128 - if (uint(i5) >= uint(i6)) { - code 130 - } else { - code 129 - } - code 131 - if (uint(i5) >= uint(i6)) { - code 133 - } else { - code 132 - } - code 134 - if (uint(i5) >= uint(i6)) { - code 136 - } else { - code 135 - } - code 137 - if (uint(i5) >= uint(i6)) { - code 139 - } else { - code 138 - } - code 140 - if (uint(i5) >= uint(i6)) { - code 142 - } else { - code 141 - } - code 143 - if (uint(i5) >= uint(i6)) { - code 145 - } else { - code 144 - } - code 146 - if (uint(i5) >= uint(i6)) { - code 148 - } else { - code 147 - } - code 149 - if (uint(i5) >= uint(i6)) { - code 151 - } else { - code 150 - } - code 152 - if (uint(i5) >= uint(i6)) { - code 154 - } else { - code 153 - } - code 155 - if (uint(i5) >= uint(i6)) { - code 157 - } else { - code 156 - } - code 158 - if (uint(i5) >= uint(i6)) { - code 160 - } else { - code 159 - } - code 161 - if (uint(i5) >= uint(i6)) { - code 163 - } else { - code 162 +L143: do { + switch (the_var) { + i2 == 0 { + break; + } + default: { + code 106 + switch (the_var) { + default: { } - code 164 - if (uint(i5) >= uint(i6)) { - code 166 - } else { - code 165 } - code 167 - if (!(i2 != 0)) { - break; + while(1) { + code 107 + switch (the_var) { + uint(i5) >= uint(i6) { + code 109 + switch (the_var) { + default: { + } + } + break; + } + default: { + code 108 + switch (the_var) { + default: { + } + } + } + } + code 110 + switch (the_var) { + uint(i5) >= uint(i6) { + code 112 + switch (the_var) { + default: { + } + } + break; + } + default: { + code 111 + switch (the_var) { + default: { + } + } + } + } + code 113 + switch (the_var) { + uint(i5) >= uint(i6) { + code 115 + switch (the_var) { + default: { + } + } + break; + } + default: { + code 114 + switch (the_var) { + default: { + } + } + } + } + code 116 + switch (the_var) { + uint(i5) >= uint(i6) { + code 118 + switch (the_var) { + default: { + } + } + break; + } + default: { + code 117 + switch (the_var) { + default: { + } + } + } + } + code 119 + switch (the_var) { + uint(i5) >= uint(i6) { + code 121 + switch (the_var) { + default: { + } + } + break; + } + default: { + code 120 + switch (the_var) { + default: { + } + } + } + } + code 122 + switch (the_var) { + uint(i5) >= uint(i6) { + code 124 + switch (the_var) { + default: { + } + } + break; + } + default: { + code 123 + switch (the_var) { + default: { + } + } + } + } + code 125 + switch (the_var) { + uint(i5) >= uint(i6) { + code 127 + switch (the_var) { + default: { + } + } + break; + } + default: { + code 126 + switch (the_var) { + default: { + } + } + } + } + code 128 + switch (the_var) { + uint(i5) >= uint(i6) { + code 130 + switch (the_var) { + default: { + } + } + break; + } + default: { + code 129 + switch (the_var) { + default: { + } + } + } + } + code 131 + switch (the_var) { + uint(i5) >= uint(i6) { + code 133 + switch (the_var) { + default: { + } + } + break; + } + default: { + code 132 + switch (the_var) { + default: { + } + } + } + } + code 134 + switch (the_var) { + uint(i5) >= uint(i6) { + code 136 + switch (the_var) { + default: { + } + } + break; + } + default: { + code 135 + switch (the_var) { + default: { + } + } + } + } + code 137 + switch (the_var) { + uint(i5) >= uint(i6) { + code 139 + switch (the_var) { + default: { + } + } + break; + } + default: { + code 138 + switch (the_var) { + default: { + } + } + } + } + code 140 + switch (the_var) { + uint(i5) >= uint(i6) { + code 142 + switch (the_var) { + default: { + } + } + break; + } + default: { + code 141 + switch (the_var) { + default: { + } + } + } + } + code 143 + switch (the_var) { + uint(i5) >= uint(i6) { + code 145 + switch (the_var) { + default: { + } + } + break; + } + default: { + code 144 + switch (the_var) { + default: { + } + } + } + } + code 146 + switch (the_var) { + uint(i5) >= uint(i6) { + code 148 + switch (the_var) { + default: { + } + } + break; + } + default: { + code 147 + switch (the_var) { + default: { + } + } + } + } + code 149 + switch (the_var) { + uint(i5) >= uint(i6) { + code 151 + switch (the_var) { + default: { + } + } + break; + } + default: { + code 150 + switch (the_var) { + default: { + } + } + } + } + code 152 + switch (the_var) { + uint(i5) >= uint(i6) { + code 154 + switch (the_var) { + default: { + } + } + break; + } + default: { + code 153 + switch (the_var) { + default: { + } + } + } + } + code 155 + switch (the_var) { + uint(i5) >= uint(i6) { + code 157 + switch (the_var) { + default: { + } + } + break; + } + default: { + code 156 + switch (the_var) { + default: { + } + } + } + } + code 158 + switch (the_var) { + uint(i5) >= uint(i6) { + code 160 + switch (the_var) { + default: { + } + } + break; + } + default: { + code 159 + switch (the_var) { + default: { + } + } + } + } + code 161 + switch (the_var) { + uint(i5) >= uint(i6) { + code 163 + switch (the_var) { + default: { + } + } + break; + } + default: { + code 162 + switch (the_var) { + default: { + } + } + } + } + code 164 + switch (the_var) { + uint(i5) >= uint(i6) { + code 166 + switch (the_var) { + default: { + } + } + break; + } + default: { + code 165 + switch (the_var) { + default: { + } + } + } + } + code 167 + switch (the_var) { + i2 != 0 { + break; + } + default: { + break L143; + } + } } } -} + } +} while(0); code 168 -if (uint(i4) >= uint(i5)) { +switch (the_var) { +uint(i4) >= uint(i5) { code 170 -} else { + switch (the_var) { + default: { + } + } + break; +} +default: { code 169 + switch (the_var) { + default: { + } + } +} } code 171 -if (i2 == 0) { +switch (the_var) { +i2 == 0 { code 183 + break; +} +default: { +} } code 172 -while(1) { +switch (the_var) { +default: { +} +} +L235: while(1) { code 173 - if (uint(i5) >= uint(i6)) { + switch (the_var) { + uint(i5) >= uint(i6) { code 175 - } else { + switch (the_var) { + default: { + } + } + break; + } + default: { code 174 + switch (the_var) { + default: { + } + } + } } code 176 - if (uint(i5) >= uint(i6)) { + switch (the_var) { + uint(i5) >= uint(i6) { code 178 - } else { + switch (the_var) { + default: { + } + } + break; + } + default: { code 177 + switch (the_var) { + default: { + } + } + } } code 179 - if (uint(i4) >= uint(i5)) { + switch (the_var) { + uint(i4) >= uint(i5) { code 181 - } else { + switch (the_var) { + default: { + } + } + break; + } + default: { code 180 + switch (the_var) { + default: { + } + } + } } code 182 - if (!(i2 != 0)) { + switch (the_var) { + i2 != 0 { break; } + default: { + break L235; + } + } } code 183 diff --git a/tools/shared.py b/tools/shared.py index 0d0f20d4f4d2e..3ee5db23a45c7 100644 --- a/tools/shared.py +++ b/tools/shared.py @@ -290,7 +290,7 @@ def check_node_version(): # we re-check sanity when the settings are changed) # We also re-check sanity and clear the cache when the version changes -EMSCRIPTEN_VERSION = '1.5.5' +EMSCRIPTEN_VERSION = '1.5.6' def generate_sanity(): return EMSCRIPTEN_VERSION + '|' + get_llvm_target() + '|' + LLVM_ROOT