diff options
Diffstat (limited to 'src/library.js')
-rw-r--r-- | src/library.js | 1242 |
1 files changed, 898 insertions, 344 deletions
diff --git a/src/library.js b/src/library.js index 344ec8d6..ab27ed35 100644 --- a/src/library.js +++ b/src/library.js @@ -63,6 +63,36 @@ LibraryManager.library = { // This is set to false when the runtime is initialized, allowing you // to modify the filesystem freely before run() is called. ignorePermissions: true, + createFileHandle: function(stream, fd) { + if (typeof stream === 'undefined') { + stream = null; + } + if (!fd) { + if (stream && stream.socket) { + for (var i = 1; i < 64; i++) { + if (!FS.streams[i]) { + fd = i; + break; + } + } + assert(fd, 'ran out of low fds for sockets'); + } else { + fd = Math.max(FS.streams.length, 64); + for (var i = FS.streams.length; i < fd; i++) { + FS.streams[i] = null; // Keep dense + } + } + } + // Close WebSocket first if we are about to replace the fd (i.e. dup2) + if (FS.streams[fd] && FS.streams[fd].socket && FS.streams[fd].socket.close) { + FS.streams[fd].socket.close(); + } + FS.streams[fd] = stream; + return fd; + }, + removeFileHandle: function(fd) { + FS.streams[fd] = null; + }, joinPath: function(parts, forceRelative) { var ret = parts[0]; for (var i = 1; i < parts.length; i++) { @@ -326,24 +356,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 +399,7 @@ LibraryManager.library = { this._chunkSize = chunkSize; this.lengthKnown = true; } - + var lazyArray = new LazyUint8Array(); Object.defineProperty(lazyArray, "length", { get: function() { @@ -560,6 +591,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] = { @@ -619,7 +651,7 @@ LibraryManager.library = { #endif allocate([ allocate( {{{ Runtime.QUANTUM_SIZE === 4 ? '[0, 0, 0, 0, _stdin, 0, 0, 0, _stdout, 0, 0, 0, _stderr, 0, 0, 0]' : '[0, _stdin, _stdout, _stderr]' }}}, - 'void*', ALLOC_DYNAMIC) ], 'void*', ALLOC_NONE, {{{ makeGlobalUse('__impure_ptr') }}}); + 'void*', ALLOC_NORMAL) ], 'void*', ALLOC_NONE, {{{ makeGlobalUse('__impure_ptr') }}}); }, quit: function() { @@ -675,10 +707,9 @@ LibraryManager.library = { ___setErrNo(ERRNO_CODES.EACCES); return 0; } - var id = FS.streams.length; // Keep dense var contents = []; for (var key in target.contents) contents.push(key); - FS.streams[id] = { + var id = FS.createFileHandle({ path: path, object: target, // An index into contents. Special values: -2 is ".", -1 is "..". @@ -695,7 +726,7 @@ LibraryManager.library = { contents: contents, // Each stream has its own area for readdir() returns. currentEntry: _malloc(___dirent_struct_layout.__size__) - }; + }); #if ASSERTIONS FS.checkStreams(); #endif @@ -1213,7 +1244,7 @@ LibraryManager.library = { finalPath = path.parentPath + '/' + path.name; } // Actually create an open stream. - var id = FS.streams.length; // Keep dense + var id; if (target.isFolder) { var entryBuffer = 0; if (___dirent_struct_layout) { @@ -1221,7 +1252,7 @@ LibraryManager.library = { } var contents = []; for (var key in target.contents) contents.push(key); - FS.streams[id] = { + id = FS.createFileHandle({ path: finalPath, object: target, // An index into contents. Special values: -2 is ".", -1 is "..". @@ -1238,9 +1269,9 @@ LibraryManager.library = { contents: contents, // Each stream has its own area for readdir() returns. currentEntry: entryBuffer - }; + }); } else { - FS.streams[id] = { + id = FS.createFileHandle({ path: finalPath, object: target, position: 0, @@ -1250,7 +1281,7 @@ LibraryManager.library = { error: false, eof: false, ungotten: [] - }; + }); } #if ASSERTIONS FS.checkStreams(); @@ -1293,10 +1324,7 @@ LibraryManager.library = { newStream[member] = stream[member]; } arg = dup2 ? arg : Math.max(arg, FS.streams.length); // dup2 wants exactly arg; fcntl wants a free descriptor >= arg - for (var i = FS.streams.length; i < arg; i++) { - FS.streams[i] = null; // Keep dense - } - FS.streams[arg] = newStream; + FS.createFileHandle(newStream, arg); #if ASSERTIONS FS.checkStreams(); #endif @@ -1773,12 +1801,14 @@ LibraryManager.library = { return bytesRead; } }, - read__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', 'pread'], + read__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', 'recv', 'pread'], read: function(fildes, buf, nbyte) { // ssize_t read(int fildes, void *buf, size_t nbyte); // http://pubs.opengroup.org/onlinepubs/000095399/functions/read.html var stream = FS.streams[fildes]; - if (!stream) { + if (stream && ('socket' in stream)) { + return _recv(fildes, buf, nbyte, 0); + } else if (!stream) { ___setErrNo(ERRNO_CODES.EBADF); return -1; } else if (!stream.isRead) { @@ -1804,6 +1834,10 @@ LibraryManager.library = { ___setErrNo(ERRNO_CODES.EIO); return -1; } + if (result === undefined && bytesRead === 0) { + ___setErrNo(ERRNO_CODES.EAGAIN); + return -1; + } if (result === null || result === undefined) break; bytesRead++; {{{ makeSetValue('buf', 'i', 'result', 'i8') }}} @@ -1969,12 +2003,14 @@ LibraryManager.library = { return i; } }, - write__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', 'pwrite'], + write__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', 'send', 'pwrite'], write: function(fildes, buf, nbyte) { // ssize_t write(int fildes, const void *buf, size_t nbyte); // http://pubs.opengroup.org/onlinepubs/000095399/functions/write.html var stream = FS.streams[fildes]; - if (!stream) { + if (stream && ('socket' in stream)) { + return _send(fildes, buf, nbyte, 0); + } else if (!stream) { ___setErrNo(ERRNO_CODES.EBADF); return -1; } else if (!stream.isWrite) { @@ -2572,7 +2608,7 @@ LibraryManager.library = { if (format[formatIndex] == 'l') { long_ = true; formatIndex++; - if(format[formatIndex] == 'l') { + if (format[formatIndex] == 'l') { longLong = true; formatIndex++; } @@ -2585,7 +2621,8 @@ LibraryManager.library = { var curr = 0; var buffer = []; // Read characters according to the format. floats are trickier, they may be in an unfloat state in the middle, then be a valid float later - if (type == 'f' || type == 'e' || type == 'g' || type == 'E') { + if (type == 'f' || type == 'e' || type == 'g' || + type == 'F' || type == 'E' || type == 'G') { var last = 0; next = get(); while (next > 0) { @@ -2607,7 +2644,7 @@ LibraryManager.library = { (type == 's' || ((type === 'd' || type == 'u' || type == 'i') && ((next >= {{{ charCode('0') }}} && next <= {{{ charCode('9') }}}) || (first && next == {{{ charCode('-') }}}))) || - (type === 'x' && (next >= {{{ charCode('0') }}} && next <= {{{ charCode('9') }}} || + ((type === 'x' || type === 'X') && (next >= {{{ charCode('0') }}} && next <= {{{ charCode('9') }}} || next >= {{{ charCode('a') }}} && next <= {{{ charCode('f') }}} || next >= {{{ charCode('A') }}} && next <= {{{ charCode('F') }}}))) && (formatIndex >= format.length || next !== format[formatIndex].charCodeAt(0))) { // Stop when we read something that is coming up @@ -2631,17 +2668,21 @@ 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') }}}; } break; + case 'X': case 'x': {{{ makeSetValue('argPtr', 0, 'parseInt(text, 16)', 'i32') }}} break; + case 'F': case 'f': + case 'E': case 'e': + case 'G': case 'g': case 'E': // fallthrough intended @@ -3576,14 +3617,14 @@ LibraryManager.library = { return -1; }, fscanf__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', - '_scanString', 'getc', 'ungetc'], + '_scanString', 'fgetc', 'fseek', 'ftell'], fscanf: function(stream, format, varargs) { // int fscanf(FILE *restrict stream, const char *restrict format, ... ); // http://pubs.opengroup.org/onlinepubs/000095399/functions/scanf.html if (FS.streams[stream]) { - var stack = []; - var get = function() { var ret = _fgetc(stream); stack.push(ret); return ret }; - var unget = function(c) { return _ungetc(stack.pop(), stream) }; + var i = _ftell(stream), SEEK_SET = 0; + var get = function () { i++; return _fgetc(stream); }; + var unget = function () { _fseek(stream, --i, SEEK_SET); }; return __scanString(format, get, unget, varargs); } else { return -1; @@ -3726,30 +3767,44 @@ LibraryManager.library = { * this implementation simply uses malloc underneath the call to * mmap. */ + var MAP_PRIVATE = 2; + var allocated = false; + if (!_mmap.mappings) _mmap.mappings = {}; + if (stream == -1) { var ptr = _malloc(num); + if (!ptr) return -1; + _memset(ptr, 0, num); + allocated = true; } else { var info = FS.streams[stream]; if (!info) return -1; var contents = info.object.contents; - contents = Array.prototype.slice.call(contents, offset, offset+num); - ptr = allocate(contents, 'i8', ALLOC_NORMAL); - } - // align to page size - var ret = ptr; - if (ptr % PAGE_SIZE != 0) { - var old = ptr; - ptr = _malloc(num + PAGE_SIZE); - ret = alignMemoryPage(ptr); - _memcpy(ret, old, num); - _free(old); - } - if (stream == -1) { - _memset(ret, 0, num); + // Only make a new copy when MAP_PRIVATE is specified. + if (flags & MAP_PRIVATE == 0) { + // We can't emulate MAP_SHARED when the file is not backed by HEAP. + assert(contents.buffer === HEAPU8.buffer); + ptr = contents.byteOffset; + allocated = false; + } else { + // Try to avoid unnecessary slices. + if (offset > 0 || offset + num < contents.length) { + if (contents.subarray) { + contents = contents.subarray(offset, offset+num); + } else { + contents = Array.prototype.slice.call(contents, offset, offset+num); + } + } + ptr = _malloc(num); + if (!ptr) return -1; + HEAPU8.set(contents, ptr); + allocated = true; + } } - _mmap.mappings[ret] = { malloc: ptr, num: num }; - return ret; + + _mmap.mappings[ptr] = { malloc: ptr, num: num, allocated: allocated }; + return ptr; }, __01mmap64_: 'mmap', @@ -3760,7 +3815,9 @@ LibraryManager.library = { if (!info) return 0; if (num == info.num) { _mmap.mappings[start] = null; - _free(info.malloc); + if (info.allocated) { + _free(info.malloc); + } } return 0; }, @@ -3781,14 +3838,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 }, @@ -3902,7 +3959,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. @@ -3953,13 +4017,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++; } @@ -3975,12 +4040,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; @@ -3993,6 +4065,7 @@ LibraryManager.library = { ok = true; } } + if (!ok) { ___setErrNo(ERRNO_CODES.EINVAL); {{{ makeStructuralReturn(['0', '0']) }}}; @@ -4004,7 +4077,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 } @@ -4451,24 +4525,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++; } @@ -4644,7 +4718,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') }}}; @@ -5072,7 +5146,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'], @@ -5560,10 +5640,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; @@ -6170,8 +6255,9 @@ LibraryManager.library = { clock_gettime__deps: ['__timespec_struct_layout'], clock_gettime: function(clk_id, tp) { // int clock_gettime(clockid_t clk_id, struct timespec *tp); - {{{ makeSetValue('tp', '___timespec_struct_layout.tv_sec', '0', 'i32') }}} - {{{ makeSetValue('tp', '___timespec_struct_layout.tv_nsec', '0', 'i32') }}} + var now = Date.now(); + {{{ makeSetValue('tp', '___timespec_struct_layout.tv_sec', 'Math.floor(now/1000)', 'i32') }}}; // seconds + {{{ makeSetValue('tp', '___timespec_struct_layout.tv_nsec', '0', 'i32') }}}; // nanoseconds - not supported return 0; }, clock_settime: function(clk_id, tp) { @@ -6631,182 +6717,274 @@ LibraryManager.library = { // ========================================================================== $ERRNO_CODES: { + EPERM: 1, + ENOENT: 2, + ESRCH: 3, + EINTR: 4, + EIO: 5, + ENXIO: 6, E2BIG: 7, - EACCES: 13, - EADDRINUSE: 98, - EADDRNOTAVAIL: 99, - EAFNOSUPPORT: 97, - EAGAIN: 11, - EALREADY: 114, + ENOEXEC: 8, EBADF: 9, - EBADMSG: 74, - EBUSY: 16, - ECANCELED: 125, ECHILD: 10, - ECONNABORTED: 103, - ECONNREFUSED: 111, - ECONNRESET: 104, - EDEADLK: 35, - EDESTADDRREQ: 89, - EDOM: 33, - EDQUOT: 122, - EEXIST: 17, + EAGAIN: 11, + EWOULDBLOCK: 11, + ENOMEM: 12, + EACCES: 13, EFAULT: 14, - EFBIG: 27, - EHOSTUNREACH: 113, - EIDRM: 43, - EILSEQ: 84, - EINPROGRESS: 115, - EINTR: 4, - EINVAL: 22, - EIO: 5, - EISCONN: 106, - EISDIR: 21, - ELOOP: 40, - EMFILE: 24, - EMLINK: 31, - EMSGSIZE: 90, - EMULTIHOP: 72, - ENAMETOOLONG: 36, - ENETDOWN: 100, - ENETRESET: 102, - ENETUNREACH: 101, - ENFILE: 23, - ENOBUFS: 105, - ENODATA: 61, + ENOTBLK: 15, + EBUSY: 16, + EEXIST: 17, + EXDEV: 18, ENODEV: 19, - ENOENT: 2, - ENOEXEC: 8, - ENOLCK: 37, - ENOLINK: 67, - ENOMEM: 12, - ENOMSG: 42, - ENOPROTOOPT: 92, - ENOSPC: 28, - ENOSR: 63, - ENOSTR: 60, - ENOSYS: 38, - ENOTCONN: 107, ENOTDIR: 20, - ENOTEMPTY: 39, - ENOTRECOVERABLE: 131, - ENOTSOCK: 88, - ENOTSUP: 95, + EISDIR: 21, + EINVAL: 22, + ENFILE: 23, + EMFILE: 24, ENOTTY: 25, - ENXIO: 6, - EOPNOTSUPP: 45, - EOVERFLOW: 75, - EOWNERDEAD: 130, - EPERM: 1, + ETXTBSY: 26, + EFBIG: 27, + ENOSPC: 28, + ESPIPE: 29, + EROFS: 30, + EMLINK: 31, EPIPE: 32, - EPROTO: 71, - EPROTONOSUPPORT: 93, - EPROTOTYPE: 91, + EDOM: 33, ERANGE: 34, - EROFS: 30, - ESPIPE: 29, - ESRCH: 3, - ESTALE: 116, + 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, - ETIMEDOUT: 110, - ETXTBSY: 26, - EWOULDBLOCK: 11, - EXDEV: 18, + 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 }, $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', - 98: 'Address already in use', - 99: 'Cannot assign requested address', - 97: 'Address family not supported by protocol', - 11: 'Resource temporarily unavailable', - 114: 'Operation already in progress', - 9: 'Bad file descriptor', - 74: 'Bad message', - 16: 'Device or resource busy', - 125: 'Operation canceled', - 10: 'No child processes', - 103: 'Software caused connection abort', - 111: 'Connection refused', - 104: 'Connection reset by peer', - 35: 'Resource deadlock avoided', - 89: 'Destination address required', - 33: 'Numerical argument out of domain', - 122: 'Disk quota exceeded', - 17: 'File exists', 14: 'Bad address', - 27: 'File too large', - 113: 'No route to host', - 43: 'Identifier removed', - 84: 'Invalid or incomplete multibyte or wide character', - 115: 'Operation now in progress', - 4: 'Interrupted system call', - 22: 'Invalid argument', - 5: 'Input/output error', - 106: 'Transport endpoint is already connected', + 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', - 40: 'Too many levels of symbolic links', - 24: 'Too many open files', - 31: 'Too many links', - 90: 'Message too long', - 72: 'Multihop attempted', - 36: 'File name too long', - 100: 'Network is down', - 102: 'Network dropped connection on reset', - 101: 'Network is unreachable', + 22: 'Invalid argument', 23: 'Too many open files in system', - 105: 'No buffer space available', - 61: 'No data available', - 19: 'No such device', - 8: 'Exec format error', - 37: 'No locks available', - 67: 'Link has been severed', - 12: 'Cannot allocate memory', - 42: 'No message of desired type', - 92: 'Protocol not available', + 24: 'Too many open files', + 25: 'Not a typewriter', + 26: 'Text file busy', + 27: 'File too large', 28: 'No space left on device', - 63: 'Out of streams resources', - 60: 'Device not a stream', - 38: 'Function not implemented', - 107: 'Transport endpoint is not connected', - 20: 'Not a directory', - 39: 'Directory not empty', - 131: 'State not recoverable', - 88: 'Socket operation on non-socket', - 95: 'Operation not supported', - 25: 'Inappropriate ioctl for device', - 6: 'No such device or address', - 45: 'Op not supported on transport endpoint', - 75: 'Value too large for defined data type', - 130: 'Owner died', - 1: 'Operation not permitted', - 32: 'Broken pipe', - 71: 'Protocol error', - 93: 'Protocol not supported', - 91: 'Protocol wrong type for socket', - 34: 'Numerical result out of range', - 30: 'Read-only file system', 29: 'Illegal seek', - 3: 'No such process', - 116: 'Stale NFS file handle', + 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', - 110: 'Connection timed out', - 26: 'Text file busy', - 18: 'Invalid cross-device link' - }, + 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', + }, + __errno_state: 0, + __setErrNo__deps: ['__errno_state'], + __setErrNo__postset: '___errno_state = Runtime.staticAlloc(4); {{{ makeSetValue("___errno_state", 0, 0, "i32") }}};', __setErrNo: function(value) { // For convenient setting and returning of errno. - if (!___setErrNo.ret) ___setErrNo.ret = allocate([0], 'i32', ALLOC_NORMAL); - {{{ makeSetValue('___setErrNo.ret', '0', 'value', 'i32') }}} + {{{ makeSetValue('___errno_state', '0', 'value', 'i32') }}} return value; }, __errno_location__deps: ['__setErrNo'], __errno_location: function() { - if (!___setErrNo.ret) { - ___setErrNo.ret = allocate([0], 'i32', ALLOC_NORMAL); - {{{ makeSetValue('___setErrNo.ret', '0', '0', 'i32') }}} - } - return ___setErrNo.ret; + return ___errno_state; }, __errno: '__errno_location', @@ -7055,6 +7233,7 @@ LibraryManager.library = { ['i32', 'h_length'], ['i8**', 'h_addr_list'], ]), + gethostbyname__deps: ['__hostent_struct_layout'], gethostbyname: function(name) { name = Pointer_stringify(name); @@ -7097,17 +7276,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'], @@ -7124,135 +7314,404 @@ 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 = FS.createFileHandle({ + addr: null, + port: null, + inQueue: new CircularBuffer(INCOMING_QUEUE_LENGTH), + header: new Uint16Array(2), + bound: false, + socket: true + }; + 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) { - |