diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/determinstic.js | 8 | ||||
-rw-r--r-- | src/intertyper.js | 15 | ||||
-rw-r--r-- | src/jsifier.js | 14 | ||||
-rw-r--r-- | src/library.js | 664 | ||||
-rw-r--r-- | src/library_browser.js | 36 | ||||
-rw-r--r-- | src/library_gl.js | 1492 | ||||
-rw-r--r-- | src/library_sdl.js | 5 | ||||
-rw-r--r-- | src/parseTools.js | 10 | ||||
-rw-r--r-- | src/preamble.js | 1 | ||||
-rw-r--r-- | src/relooper/Relooper.cpp | 127 | ||||
-rw-r--r-- | src/relooper/Relooper.h | 7 | ||||
-rw-r--r-- | src/relooper/fuzzer.py | 4 | ||||
-rw-r--r-- | src/relooper/test.cpp | 41 | ||||
-rw-r--r-- | src/relooper/test.txt | 27 | ||||
-rw-r--r-- | src/relooper/test4.txt | 1 | ||||
-rw-r--r-- | src/relooper/test_debug.txt | 76 | ||||
-rw-r--r-- | src/relooper/test_fuzz1.txt | 11 | ||||
-rw-r--r-- | src/relooper/test_fuzz5.txt | 24 | ||||
-rw-r--r-- | src/relooper/test_fuzz6.txt | 1 | ||||
-rwxr-xr-x | src/relooper/testit.sh | 2 | ||||
-rwxr-xr-x | src/relooper/updateit.sh | 2 | ||||
-rw-r--r-- | src/settings.js | 7 | ||||
-rw-r--r-- | src/shell.js | 1 | ||||
-rw-r--r-- | src/socket.io.js | 3870 | ||||
-rw-r--r-- | src/wrtcp.js | 821 |
25 files changed, 6821 insertions, 446 deletions
diff --git a/src/determinstic.js b/src/determinstic.js index 91f98ed9..1ec0bbfe 100644 --- a/src/determinstic.js +++ b/src/determinstic.js @@ -10,3 +10,11 @@ Date.now = function() { }; performance.now = Date.now; +function hashMemory(id) { + var ret = 0; + for (var i = 0; i < HEAPU8.length; i++) { + ret = (ret*17 + HEAPU8[i])|0; + } + print(id + ':' + ret); +} + diff --git a/src/intertyper.js b/src/intertyper.js index 445c37f4..6da30ae8 100644 --- a/src/intertyper.js +++ b/src/intertyper.js @@ -537,12 +537,17 @@ function intertyper(data, sidePass, baseLineNums) { }); } } else if (!external) { - if (item.tokens[3].text == 'c') - item.tokens.splice(3, 1); - if (item.tokens[3].text in PARSABLE_LLVM_FUNCTIONS) { - ret.value = parseLLVMFunctionCall(item.tokens.slice(2)); + if (item.tokens[3] && item.tokens[3].text != ';') { + if (item.tokens[3].text == 'c') { + item.tokens.splice(3, 1); + } + if (item.tokens[3].text in PARSABLE_LLVM_FUNCTIONS) { + ret.value = parseLLVMFunctionCall(item.tokens.slice(2)); + } else { + ret.value = scanConst(item.tokens[3], ret.type); + } } else { - ret.value = scanConst(item.tokens[3], ret.type); + ret.value = { intertype: 'value', ident: '0', value: '0', type: ret.type }; } } return [ret]; diff --git a/src/jsifier.js b/src/jsifier.js index 2c83d036..156fd65d 100644 --- a/src/jsifier.js +++ b/src/jsifier.js @@ -12,6 +12,8 @@ var RELOOP_IGNORED_LASTS = set('return', 'unreachable', 'resume'); var addedLibraryItems = {}; var asmLibraryFunctions = []; +var SETJMP_LABEL = -1; + // JSifier function JSify(data, functionsOnly, givenFunctions) { var mainPass = !functionsOnly; @@ -458,6 +460,7 @@ function JSify(data, functionsOnly, givenFunctions) { } else { ident = '_' + ident; } + if (VERBOSE) printErr('adding ' + ident + ' and deps ' + deps); var depsText = (deps ? '\n' + deps.map(addFromLibrary).filter(function(x) { return x != '' }).join('\n') : ''); var contentText = isFunction ? snippet : ('var ' + ident + '=' + snippet + ';'); if (ASM_JS) { @@ -730,7 +733,7 @@ function JSify(data, functionsOnly, givenFunctions) { }).join('\n') + '\n'; if (func.setjmpTable && ASM_JS) { // emit a label in which we write to the proper local variable, before jumping to the actual label - ret += ' case -1111: '; + ret += ' case ' + SETJMP_LABEL + ': '; ret += func.setjmpTable.map(function(triple) { // original label, label we created for right after the setjmp, variable setjmp result goes into return 'if ((setjmpLabel|0) == ' + getLabelId(triple.oldLabel) + ') { ' + triple.assignTo + ' = threwValue; label = ' + triple.newLabel + ' }\n'; }).join(' else '); @@ -1030,13 +1033,13 @@ function JSify(data, functionsOnly, givenFunctions) { } for (var i = 0; i < idents.length; i++) { if (keys(deps[idents[i]]).length == 0) { - pre = 'var ' + idents[i] + ' = ' + valueJSes[idents[i]] + ';' + pre; + post = 'var ' + idents[i] + ' = ' + valueJSes[idents[i]] + ';' + post; remove(idents[i]); continue mainLoop; } } // If we got here, we have circular dependencies, and must break at least one. - pre = 'var ' + idents[0] + '$phi = ' + valueJSes[idents[0]] + ';' + pre; + pre += 'var ' + idents[0] + '$phi = ' + valueJSes[idents[0]] + ';'; post += 'var ' + idents[0] + ' = ' + idents[0] + '$phi;'; remove(idents[0]); } @@ -1160,6 +1163,7 @@ function JSify(data, functionsOnly, givenFunctions) { return ret + ';'; }); makeFuncLineActor('resume', function(item) { + if (DISABLE_EXCEPTION_CATCHING) return 'abort()'; if (item.ident == 0) { // No exception to resume, so we can just bail. // This is related to issue #917 and http://llvm.org/PR15518 @@ -1488,9 +1492,9 @@ function JSify(data, functionsOnly, givenFunctions) { } if (ASM_JS && funcData.setjmpTable) { - // check if a longjmp was done. If a setjmp happened, check if ours. If ours, go to -111 to handle it. + // check if a longjmp was done. If a setjmp happened, check if ours. If ours, go to a special label to handle it. // otherwise, just return - the call to us must also have been an invoke, so the setjmp propagates that way - ret += '; if (((__THREW__|0) != 0) & ((threwValue|0) != 0)) { setjmpLabel = ' + asmCoercion('_testSetjmp(' + makeGetValue('__THREW__', 0, 'i32') + ', setjmpTable)', 'i32') + '; if ((setjmpLabel|0) > 0) { label = -1111; break } else return ' + (funcData.returnType != 'void' ? asmCoercion('0', funcData.returnType) : '') + ' } __THREW__ = threwValue = 0;\n'; + ret += '; if (((__THREW__|0) != 0) & ((threwValue|0) != 0)) { setjmpLabel = ' + asmCoercion('_testSetjmp(' + makeGetValue('__THREW__', 0, 'i32') + ', setjmpTable)', 'i32') + '; if ((setjmpLabel|0) > 0) { label = ' + SETJMP_LABEL + '; break } else return ' + (funcData.returnType != 'void' ? asmCoercion('0', funcData.returnType) : '') + ' } __THREW__ = threwValue = 0;\n'; } return ret; diff --git a/src/library.js b/src/library.js index 27b79b32..08cb3baf 100644 --- a/src/library.js +++ b/src/library.js @@ -326,24 +326,25 @@ LibraryManager.library = { #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) { @@ -368,7 +369,7 @@ LibraryManager.library = { this._chunkSize = chunkSize; this.lengthKnown = true; } - + var lazyArray = new LazyUint8Array(); Object.defineProperty(lazyArray, "length", { get: function() { @@ -560,6 +561,7 @@ LibraryManager.library = { var stdout = FS.createDevice(devFolder, 'stdout', null, output); var stderr = FS.createDevice(devFolder, 'stderr', null, error); FS.createDevice(devFolder, 'tty', input, output); + FS.createDevice(devFolder, 'null', function(){}, function(){}); // Create default streams. FS.streams[1] = { @@ -2572,7 +2574,7 @@ LibraryManager.library = { if (format[formatIndex] == 'l') { long_ = true; formatIndex++; - if(format[formatIndex] == 'l') { + if (format[formatIndex] == 'l') { longLong = true; formatIndex++; } @@ -2631,7 +2633,7 @@ LibraryManager.library = { case 'd': case 'u': case 'i': if (half) { {{{ makeSetValue('argPtr', 0, 'parseInt(text, 10)', 'i16') }}}; - } else if(longLong) { + } else if (longLong) { {{{ makeSetValue('argPtr', 0, 'parseInt(text, 10)', 'i64') }}}; } else { {{{ makeSetValue('argPtr', 0, 'parseInt(text, 10)', 'i32') }}}; @@ -3797,14 +3799,14 @@ LibraryManager.library = { * implementation (replaced by dlmalloc normally) so * not an issue. */ -#if ASSERTIONS +#if ASSERTIONS == 2 Runtime.warnOnce('using stub malloc (reference it from C to have the real one included)'); #endif var ptr = Runtime.dynamicAlloc(bytes + 8); return (ptr+8) & 0xFFFFFFF8; }, free: function() { -#if ASSERTIONS +#if ASSERTIONS == 2 Runtime.warnOnce('using stub free (reference it from C to have the real one included)'); #endif }, @@ -3918,7 +3920,14 @@ LibraryManager.library = { str++; } } - } + } else if (finalBase==16) { + if ({{{ makeGetValue('str', 0, 'i8') }}} == {{{ charCode('0') }}}) { + if ({{{ makeGetValue('str+1', 0, 'i8') }}} == {{{ charCode('x') }}} || + {{{ makeGetValue('str+1', 0, 'i8') }}} == {{{ charCode('X') }}}) { + str += 2; + } + } + } if (!finalBase) finalBase = 10; // Get digits. @@ -3969,13 +3978,14 @@ LibraryManager.library = { #if USE_TYPED_ARRAYS == 2 _parseInt64__deps: ['isspace', '__setErrNo', '$ERRNO_CODES', function() { Types.preciseI64MathUsed = 1 }], _parseInt64: function(str, endptr, base, min, max, unsign) { - var start = str; + var isNegative = false; // Skip space. while (_isspace({{{ makeGetValue('str', 0, 'i8') }}})) str++; - + // Check for a plus/minus sign. if ({{{ makeGetValue('str', 0, 'i8') }}} == {{{ charCode('-') }}}) { str++; + isNegative = true; } else if ({{{ makeGetValue('str', 0, 'i8') }}} == {{{ charCode('+') }}}) { str++; } @@ -3991,12 +4001,19 @@ LibraryManager.library = { str += 2; } else { finalBase = 8; - str++; ok = true; // we saw an initial zero, perhaps the entire thing is just "0" } } - } + } else if (finalBase==16) { + if ({{{ makeGetValue('str', 0, 'i8') }}} == {{{ charCode('0') }}}) { + if ({{{ makeGetValue('str+1', 0, 'i8') }}} == {{{ charCode('x') }}} || + {{{ makeGetValue('str+1', 0, 'i8') }}} == {{{ charCode('X') }}}) { + str += 2; + } + } + } if (!finalBase) finalBase = 10; + start = str; // Get digits. var chr; @@ -4009,6 +4026,7 @@ LibraryManager.library = { ok = true; } } + if (!ok) { ___setErrNo(ERRNO_CODES.EINVAL); {{{ makeStructuralReturn(['0', '0']) }}}; @@ -4020,7 +4038,8 @@ LibraryManager.library = { } try { - i64Math.fromString(Pointer_stringify(start, str - start), finalBase, min, max, unsign); + var numberString = isNegative ? '-'+Pointer_stringify(start, str - start) : Pointer_stringify(start, str - start); + i64Math.fromString(numberString, finalBase, min, max, unsign); } catch(e) { ___setErrNo(ERRNO_CODES.ERANGE); // not quite correct } @@ -4467,24 +4486,24 @@ LibraryManager.library = { } return pdest|0; }, - + strlwr__deps:['tolower'], strlwr: function(pstr){ var i = 0; while(1) { var x = {{{ makeGetValue('pstr', 'i', 'i8') }}}; - if(x == 0) break; + if (x == 0) break; {{{ makeSetValue('pstr', 'i', '_tolower(x)', 'i8') }}}; i++; } }, - + strupr__deps:['toupper'], strupr: function(pstr){ var i = 0; while(1) { var x = {{{ makeGetValue('pstr', 'i', 'i8') }}}; - if(x == 0) break; + if (x == 0) break; {{{ makeSetValue('pstr', 'i', '_toupper(x)', 'i8') }}}; i++; } @@ -4660,7 +4679,7 @@ LibraryManager.library = { if (size < 0) { size = 0; } - + var newStr = _malloc(size + 1); {{{ makeCopyValues('newStr', 'ptr', 'size', 'null', null, 1) }}}; {{{ makeSetValue('newStr', 'size', '0', 'i8') }}}; @@ -5088,7 +5107,13 @@ LibraryManager.library = { return _malloc(size); }, __cxa_free_exception: function(ptr) { - return _free(ptr); + try { + return _free(ptr); + } catch(e) { // XXX FIXME +#if ASSERTIONS + Module.printErr('exception during cxa_free_exception: ' + e); +#endif + } }, __cxa_throw__sig: 'viii', __cxa_throw__deps: ['llvm_eh_exception', '_ZSt18uncaught_exceptionv', '__cxa_find_matching_catch'], @@ -5576,10 +5601,15 @@ LibraryManager.library = { frexp: function(x, exp_addr) { var sig = 0, exp_ = 0; if (x !== 0) { + var sign = 1; + if (x < 0) { + x = -x; + sign = -1; + } var raw_exp = Math.log(x)/Math.log(2); exp_ = Math.ceil(raw_exp); if (exp_ === raw_exp) exp_ += 1; - sig = x/Math.pow(2, exp_); + sig = sign*x/Math.pow(2, exp_); } {{{ makeSetValue('exp_addr', 0, 'exp_', 'i32') }}} return sig; @@ -6907,7 +6937,7 @@ LibraryManager.library = { }, __errno_state: 0, __setErrNo__deps: ['__errno_state'], - __setErrNo__postset: '___errno_state = Runtime.staticAlloc(4);', + __setErrNo__postset: '___errno_state = Runtime.staticAlloc(4); {{{ makeSetValue("___errno_state", 0, 0, "i32") }}};', __setErrNo: function(value) { // For convenient setting and returning of errno. {{{ makeSetValue('___errno_state', '0', 'value', 'i32') }}} @@ -7164,6 +7194,7 @@ LibraryManager.library = { ['i32', 'h_length'], ['i8**', 'h_addr_list'], ]), + gethostbyname__deps: ['__hostent_struct_layout'], gethostbyname: function(name) { name = Pointer_stringify(name); @@ -7206,17 +7237,28 @@ LibraryManager.library = { // sockets. Note that the implementation assumes all sockets are always // nonblocking // ========================================================================== - +#if SOCKET_WEBRTC + $Sockets__deps: ['__setErrNo', '$ERRNO_CODES', + function() { return 'var SocketIO = ' + read('socket.io.js') + ';\n' }, + function() { return 'var Peer = ' + read('wrtcp.js') + ';\n' }], +#else $Sockets__deps: ['__setErrNo', '$ERRNO_CODES'], +#endif $Sockets: { - BACKEND_WEBSOCKETS: 0, - BACKEND_WEBRTC: 1, BUFFER_SIZE: 10*1024, // initial size MAX_BUFFER_SIZE: 10*1024*1024, // maximum size we will grow the buffer - backend: 0, // default to websockets nextFd: 1, fds: {}, + nextport: 1, + maxport: 65535, + peer: null, + connections: {}, + portmap: {}, + localAddr: 0xfe00000a, // Local address is always 10.0.0.254 + addrPool: [ 0x0200000a, 0x0300000a, 0x0400000a, 0x0500000a, + 0x0600000a, 0x0700000a, 0x0800000a, 0x0900000a, 0x0a00000a, + 0x0b00000a, 0x0c00000a, 0x0d00000a, 0x0e00000a], /* 0x0100000a is reserved */ sockaddr_in_layout: Runtime.generateStructInfo([ ['i32', 'sin_family'], ['i16', 'sin_port'], @@ -7233,114 +7275,386 @@ LibraryManager.library = { ['i32', 'msg_controllen'], ['i32', 'msg_flags'], ]), + }, - backends: { - 0: { // websockets - connect: function(info) { - console.log('opening ws://' + info.host + ':' + info.port); - info.socket = new WebSocket('ws://' + info.host + ':' + info.port, ['binary']); - info.socket.binaryType = 'arraybuffer'; +#if SOCKET_WEBRTC + /* WebRTC sockets supports several options on the Module object. - var i32Temp = new Uint32Array(1); - var i8Temp = new Uint8Array(i32Temp.buffer); + * Module['host']: true if this peer is hosting, false otherwise + * Module['webrtc']['broker']: hostname for the p2p broker that this peer should use + * Module['webrtc']['session']: p2p session for that this peer will join, or undefined if this peer is hosting + * Module['webrtc']['hostOptions']: options to pass into p2p library if this peer is hosting + * Module['webrtc']['onpeer']: function(peer, route), invoked when this peer is ready to connect + * Module['webrtc']['onconnect']: function(peer), invoked when a new peer connection is ready + * Module['webrtc']['ondisconnect']: function(peer), invoked when an existing connection is closed + * Module['webrtc']['onerror']: function(error), invoked when an error occurs + */ + socket__deps: ['$Sockets'], + socket: function(family, type, protocol) { + var fd = Sockets.nextFd++; + assert(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 + } - info.inQueue = []; - info.hasData = function() { return info.inQueue.length > 0 } - if (!info.stream) { - var partialBuffer = null; // in datagram mode, inQueue contains full dgram messages; this buffers incomplete data. Must begin with the beginning of a message + // Open the peer connection if we don't have it already + if (null == Sockets.peer) { + var host = Module['host']; + var broker = Module['webrtc']['broker']; + var session = Module['webrtc']['session']; + var peer = new Peer(broker); + var listenOptions = Module['webrtc']['hostOptions'] || {}; + peer.onconnection = function(connection) { + console.log('connected'); + var addr; + /* If this peer is connecting to the host, assign 10.0.0.1 to the host so it can be + reached at a known address. + */ + // Assign 10.0.0.1 to the host + if (session && session === connection['route']) { + addr = 0x0100000a; // 10.0.0.1 + } else { + addr = Sockets.addrPool.shift(); + } + connection['addr'] = addr; + Sockets.connections[addr] = connection; + connection.ondisconnect = function() { + console.log('disconnect'); + // Don't return the host address (10.0.0.1) to the pool + if (!(session && session === Sockets.connections[addr]['route'])) { + Sockets.addrPool.push(addr); } + delete Sockets.connections[addr]; - info.socket.onmessage = function(event) { - assert(typeof event.data !== 'string' && event.data.byteLength); // must get binary data! - var data = new Uint8Array(event.data); // make a typed array view on the array buffer + if (Module['webrtc']['ondisconnect'] && 'function' === typeof Module['webrtc']['ondisconnect']) { + Module['webrtc']['ondisconnect'](peer); + } + }; + connection.onerror = function(error) { + if (Module['webrtc']['onerror'] && 'function' === typeof Module['webrtc']['onerror']) { + Module['webrtc']['onerror'](error); + } + }; + connection.onmessage = function(label, message) { + if ('unreliable' === label) { + handleMessage(addr, message.data); + } + } + + if (Module['webrtc']['onconnect'] && 'function' === typeof Module['webrtc']['onconnect']) { + Module['webrtc']['onconnect'](peer); + } + }; + peer.onpending = function(pending) { + console.log('pending from: ', pending['route'], '; initiated by: ', (pending['incoming']) ? 'remote' : 'local'); + }; + peer.onerror = function(error) { + console.error(error); + }; + peer.onroute = function(route) { + if (Module['webrtc']['onpeer'] && 'function' === typeof Module['webrtc']['onpeer']) { + Module['webrtc']['onpeer'](peer, route); + } + }; + function handleMessage(addr, message) { #if SOCKET_DEBUG - Module.print(['onmessage', data.length, '|', Array.prototype.slice.call(data)]); + Module.print("received " + message.byteLength + " raw bytes"); #endif - if (info.stream) { - info.inQueue.push(data); - } else { - // we added headers with message sizes, read those to find discrete messages - if (partialBuffer) { - // append to the partial buffer - var newBuffer = new Uint8Array(partialBuffer.length + data.length); - newBuffer.set(partialBuffer); - newBuffer.set(data, partialBuffer.length); - // forget the partial buffer and work on data - data = newBuffer; - partialBuffer = null; - } - var currPos = 0; - while (currPos+4 < data.length) { - i8Temp.set(data.subarray(currPos, currPos+4)); - var currLen = i32Temp[0]; - assert(currLen > 0); - if (currPos + 4 + currLen > data.length) { - break; // not enough data has arrived - } - currPos += 4; + var header = new Uint16Array(message, 0, 2); + if (Sockets.portmap[header[1]]) { + Sockets.portmap[header[1]].inQueue.push([addr, message]); + } else { + console.log("unable to deliver message: ", addr, header[1], message); + } + } + window.onbeforeunload = function() { + var ids = Object.keys(Sockets.connections); + ids.forEach(function(id) { + Sockets.connections[id].close(); + }); + } + Sockets.peer = peer; + } + + var INCOMING_QUEUE_LENGTH = 64; + + function CircularBuffer(max_length) { + var buffer = new Array(++ max_length); + var head = 0; + var tail = 0; + var length = 0; + + return { + push: function(element) { + buffer[tail ++] = element; + length = Math.min(++ length, max_length - 1); + tail = tail % max_length; + if (tail === head) { + head = (head + 1) % max_length; + } + }, + shift: function(element) { + if (length < 1) return undefined; + + var element = buffer[head]; + -- length; + head = (head + 1) % max_length; + return element; + }, + length: function() { + return length; + } + }; + }; + + Sockets.fds[fd] = { + addr: null, + port: null, + inQueue: new CircularBuffer(INCOMING_QUEUE_LENGTH), + header: new Uint16Array(2), + bound: false + }; + return fd; + }, + + mkport__deps: ['$Sockets'], + mkport: function() { + for(var i = 0; i < Sockets.maxport; ++ i) { + var port = Sockets.nextport ++; + Sockets.nextport = (Sockets.nextport > Sockets.maxport) ? 1 : Sockets.nextport; + if (!Sockets.portmap[port]) { + return port; + } + } + assert(false, 'all available ports are in use!'); + }, + + connect: function() { + // Stub: connection-oriented sockets are not supported yet. + }, + + bind__deps: ['$Sockets', '_inet_ntop_raw', 'ntohs', 'mkport'], + bind: function(fd, addr, addrlen) { + var info = Sockets.fds[fd]; + if (!info) return -1; + if (addr) { + info.port = _ntohs(getValue(addr + Sockets.sockaddr_in_layout.sin_port, 'i16')); + // info.addr = getValue(addr + Sockets.sockaddr_in_layout.sin_addr, 'i32'); + } + if (!info.port) { + info.port = _mkport(); + } + info.addr = Sockets.localAddr; // 10.0.0.254 + info.host = __inet_ntop_raw(info.addr); + info.close = function() { + Sockets.portmap[info.port] = undefined; + } + Sockets.portmap[info.port] = info; + console.log("bind: ", info.host, info.port); + info.bound = true; + }, + + sendmsg__deps: ['$Sockets', 'bind', '_inet_ntop_raw', 'ntohs'], + sendmsg: function(fd, msg, flags) { + var info = Sockets.fds[fd]; + if (!info) return -1; + // if we are not connected, use the address info in the message + if (!info.bound) { + _bind(fd); + } + + var name = {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_name', '*') }}}; + assert(name, 'sendmsg on non-connected socket, and no name/address in the message'); + var port = _ntohs(getValue(name + Sockets.sockaddr_in_layout.sin_port, 'i16')); + var addr = getValue(name + Sockets.sockaddr_in_layout.sin_addr, 'i32'); + var connection = Sockets.connections[addr]; + // var host = __inet_ntop_raw(addr); + + if (!(connection && connection.connected)) { + ___setErrNo(ERRNO_CODES.EWOULDBLOCK); + return -1; + } + + var iov = {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_iov', 'i8*') }}}; + var num = {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_iovlen', 'i32') }}}; #if SOCKET_DEBUG - Module.print(['onmessage message', currLen, '|', Array.prototype.slice.call(data.subarray(currPos, currPos+currLen))]); + Module.print('sendmsg vecs: ' + num); #endif - info.inQueue.push(data.subarray(currPos, currPos+currLen)); - currPos += currLen; - } - // If data remains, buffer it - if (currPos < data.length) { - partialBuffer = data.subarray(currPos); - } - } - } - function send(data) { - // TODO: if browser accepts views, can optimize this + var totalSize = 0; + for (var i = 0; i < num; i++) { + totalSize += {{{ makeGetValue('iov', '8*i + 4', 'i32') }}}; + } + var data = new Uint8Array(totalSize); + var ret = 0; + for (var i = 0; i < num; i++) { + var currNum = {{{ makeGetValue('iov', '8*i + 4', 'i32') }}}; #if SOCKET_DEBUG - Module.print('sender actually sending ' + Array.prototype.slice.call(data)); + Module.print('sendmsg curr size: ' + currNum); #endif - // ok to use the underlying buffer, we created data and know that the buffer starts at the beginning - info.socket.send(data.buffer); - } - var outQueue = []; - var intervalling = false, interval; - function trySend() { - if (info.socket.readyState != info.socket.OPEN) { - if (!intervalling) { - intervalling = true; - console.log('waiting for socket in order to send'); - interval = setInterval(trySend, 100); - } - return; - } - for (var i = 0; i < outQueue.length; i++) { - send(outQueue[i]); - } - outQueue.length = 0; - if (intervalling) { - intervalling = false; - clearInterval(interval); - } + if (!currNum) continue; + var currBuf = {{{ makeGetValue('iov', '8*i', 'i8*') }}}; + data.set(HEAPU8.subarray(currBuf, currBuf+currNum), ret); + ret += currNum; + } + + info.header[0] = info.port; // src port + info.header[1] = port; // dst port +#if SOCKET_DEBUG + Module.print('sendmsg port: ' + info.header[0] + ' -> ' + info.header[1]); + Module.print('sendmsg bytes: ' + data.length + ' | ' + Array.prototype.slice.call(data)); +#endif + var buffer = new Uint8Array(info.header.byteLength + data.byteLength); + buffer.set(new Uint8Array(info.header.buffer)); + buffer.set(data, info.header.byteLength); + + connection.send('unreliable', buffer.buffer); + }, + + recvmsg__deps: ['$Sockets', 'bind', '__setErrNo', '$ERRNO_CODES', 'htons'], + recvmsg: function(fd, msg, flags) { + var info = Sockets.fds[fd]; + if (!info) return -1; + // if we are not connected, use the address info in the message + if (!info.port) { + console.log('recvmsg on unbound socket'); + assert(false, 'cannot receive on unbound socket'); + } + if (info.inQueue.length() == 0) { + ___setErrNo(ERRNO_CODES.EWOULDBLOCK); + return -1; + } + + var entry = info.inQueue.shift(); + var addr = entry[0]; + var message = entry[1]; + var header = new Uint16Array(message, 0, info.header.length); + var buffer = new Uint8Array(message, info.header.byteLength); + + var bytes = buffer.length; +#if SOCKET_DEBUG + Module.print('recvmsg port: ' + header[1] + ' <- ' + header[0]); + Module.print('recvmsg bytes: ' + bytes + ' | ' + Array.prototype.slice.call(buffer)); +#endif + // write source + var name = {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_name', '*') }}}; + {{{ makeSetValue('name', 'Sockets.sockaddr_in_layout.sin_addr', 'addr', 'i32') }}}; + {{{ makeSetValue('name', 'Sockets.sockaddr_in_layout.sin_port', '_htons(header[0])', 'i16') }}}; + // write data + var ret = bytes; + var iov = {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_iov', 'i8*') }}}; + var num = {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_iovlen', 'i32') }}}; + var bufferPos = 0; + for (var i = 0; i < num && bytes > 0; i++) { + var currNum = {{{ makeGetValue('iov', '8*i + 4', 'i32') }}}; +#if SOCKET_DEBUG + Module.print('recvmsg loop ' + [i, num, bytes, currNum]); +#endif + if (!currNum) continue; + currNum = Math.min(currNum, bytes); // XXX what should happen when we partially fill a buffer..? + bytes -= currNum; + var currBuf = {{{ makeGetValue('iov', '8*i', 'i8*') }}}; +#if SOCKET_DEBUG + Module.print('recvmsg call recv ' + currNum); +#endif + HEAPU8.set(buffer.subarray(bufferPos, bufferPos + currNum), currBuf); + bufferPos += currNum; + } + return ret; + }, + + shutdown: function(fd, how) { + var info = Sockets.fds[fd]; + if (!info) return -1; + info.close(); + Sockets.fds[fd] = null; + }, + + ioctl: function(fd, request, varargs) { + var info = Sockets.fds[fd]; + if (!info) return -1; + var bytes = 0; + if (info.hasData()) { + bytes = info.inQueue[0].length; + } + var dest = {{{ makeGetValue('varargs', '0', 'i32') }}}; + {{{ makeSetValue('dest', '0', 'bytes', 'i32') }}}; + return 0; + }, + + setsockopt: function(d, level, optname, optval, optlen) { + console.log('ignoring setsockopt command'); + return 0; + }, + + accept: function(fd, addr, addrlen) { + // TODO: webrtc queued incoming connections, etc. + // For now, the model is that bind does a connect, and we "accept" that one connection, + // which has host:port the same as ours. We also return the same socket fd. + var info |