aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/determinstic.js8
-rw-r--r--src/intertyper.js15
-rw-r--r--src/jsifier.js14
-rw-r--r--src/library.js664
-rw-r--r--src/library_browser.js36
-rw-r--r--src/library_gl.js1492
-rw-r--r--src/library_sdl.js5
-rw-r--r--src/parseTools.js10
-rw-r--r--src/preamble.js1
-rw-r--r--src/relooper/Relooper.cpp127
-rw-r--r--src/relooper/Relooper.h7
-rw-r--r--src/relooper/fuzzer.py4
-rw-r--r--src/relooper/test.cpp41
-rw-r--r--src/relooper/test.txt27
-rw-r--r--src/relooper/test4.txt1
-rw-r--r--src/relooper/test_debug.txt76
-rw-r--r--src/relooper/test_fuzz1.txt11
-rw-r--r--src/relooper/test_fuzz5.txt24
-rw-r--r--src/relooper/test_fuzz6.txt1
-rwxr-xr-xsrc/relooper/testit.sh2
-rwxr-xr-xsrc/relooper/updateit.sh2
-rw-r--r--src/settings.js7
-rw-r--r--src/shell.js1
-rw-r--r--src/socket.io.js3870
-rw-r--r--src/wrtcp.js821
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