diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/deps_info.json | 7 | ||||
-rw-r--r-- | src/embind/emval.js | 36 | ||||
-rw-r--r-- | src/headlessCanvas.js | 1 | ||||
-rw-r--r-- | src/jsifier.js | 72 | ||||
-rw-r--r-- | src/library.js | 320 | ||||
-rw-r--r-- | src/library_browser.js | 22 | ||||
-rw-r--r-- | src/library_egl.js | 2 | ||||
-rw-r--r-- | src/library_fs.js | 213 | ||||
-rw-r--r-- | src/library_gl.js | 198 | ||||
-rw-r--r-- | src/library_glfw.js | 8 | ||||
-rw-r--r-- | src/library_glut.js | 2 | ||||
-rw-r--r-- | src/library_html5.js | 19 | ||||
-rw-r--r-- | src/library_idbfs.js | 324 | ||||
-rw-r--r-- | src/library_openal.js | 195 | ||||
-rw-r--r-- | src/library_sdl.js | 160 | ||||
-rw-r--r-- | src/parseTools.js | 4 | ||||
-rw-r--r-- | src/postamble.js | 4 | ||||
-rw-r--r-- | src/preamble.js | 9 | ||||
-rw-r--r-- | src/runtime.js | 4 | ||||
-rw-r--r-- | src/settings.js | 16 | ||||
-rw-r--r-- | src/struct_info.json | 4 |
21 files changed, 1028 insertions, 592 deletions
diff --git a/src/deps_info.json b/src/deps_info.json new file mode 100644 index 00000000..b38ffd00 --- /dev/null +++ b/src/deps_info.json @@ -0,0 +1,7 @@ +{ + "uuid_compare": ["memcmp"], + "SDL_Init": ["malloc", "free"], + "SDL_GL_GetProcAddress": ["emscripten_GetProcAddress"], + "eglGetProcAddress": ["emscripten_GetProcAddress"] +} + diff --git a/src/embind/emval.js b/src/embind/emval.js index 0d075188..039f1d61 100644 --- a/src/embind/emval.js +++ b/src/embind/emval.js @@ -3,7 +3,7 @@ /*global new_*/ /*global createNamedFunction*/ /*global readLatin1String, writeStringToMemory*/ -/*global requireRegisteredType, throwBindingError*/ +/*global requireRegisteredType, throwBindingError, runDestructors*/ /*jslint sub:true*/ /* The symbols 'fromWireType' and 'toWireType' must be accessed via array notation to be closure-safe since craftInvokerFunction crafts functions as strings that can't be closured. */ var Module = Module || {}; @@ -79,6 +79,12 @@ function __emval_decref(handle) { } } +function __emval_run_destructors(handle) { + var destructors = _emval_handle_array[handle].value; + runDestructors(destructors); + __emval_decref(handle); +} + function __emval_new_array() { return __emval_register([]); } @@ -199,11 +205,12 @@ function __emval_set_property(handle, key, value) { _emval_handle_array[handle].value[_emval_handle_array[key].value] = _emval_handle_array[value].value; } -function __emval_as(handle, returnType) { +function __emval_as(handle, returnType, destructorsRef) { requireHandle(handle); returnType = requireRegisteredType(returnType, 'emval::as'); var destructors = []; - // caller owns destructing + var rd = __emval_register(destructors); + HEAP32[destructorsRef >> 2] = rd; return returnType['toWireType'](destructors, _emval_handle_array[handle].value); } @@ -242,14 +249,20 @@ function lookupTypes(argCount, argTypes, argWireTypes) { return a; } +function allocateDestructors(destructorsRef) { + var destructors = []; + HEAP32[destructorsRef >> 2] = __emval_register(destructors); + return destructors; +} + function __emval_get_method_caller(argCount, argTypes) { var types = lookupTypes(argCount, argTypes); var retType = types[0]; var signatureName = retType.name + "_$" + types.slice(1).map(function (t) { return t.name; }).join("_") + "$"; - var args1 = ["addFunction", "createNamedFunction", "requireHandle", "getStringOrSymbol", "_emval_handle_array", "retType"]; - var args2 = [Runtime.addFunction, createNamedFunction, requireHandle, getStringOrSymbol, _emval_handle_array, retType]; + var args1 = ["addFunction", "createNamedFunction", "requireHandle", "getStringOrSymbol", "_emval_handle_array", "retType", "allocateDestructors"]; + var args2 = [Runtime.addFunction, createNamedFunction, requireHandle, getStringOrSymbol, _emval_handle_array, retType, allocateDestructors]; var argsList = ""; // 'arg0, arg1, arg2, ... , argN' var argsListWired = ""; // 'arg0Wired, ..., argNWired' @@ -261,16 +274,17 @@ function __emval_get_method_caller(argCount, argTypes) { } var invokerFnBody = - "return addFunction(createNamedFunction('" + signatureName + "', function (handle, name" + argsListWired + ") {\n" + - "requireHandle(handle);\n" + - "name = getStringOrSymbol(name);\n"; + "return addFunction(createNamedFunction('" + signatureName + "', function (handle, name, destructorsRef" + argsListWired + ") {\n" + + " requireHandle(handle);\n" + + " name = getStringOrSymbol(name);\n"; for (var i = 0; i < argCount - 1; ++i) { - invokerFnBody += "var arg" + i + " = argType" + i + ".fromWireType(arg" + i + "Wired);\n"; + invokerFnBody += " var arg" + i + " = argType" + i + ".fromWireType(arg" + i + "Wired);\n"; } invokerFnBody += - "var obj = _emval_handle_array[handle].value;\n" + - "return retType.toWireType(null, obj[name](" + argsList + "));\n" + + " var obj = _emval_handle_array[handle].value;\n" + + " var rv = obj[name](" + argsList + ");\n" + + " return retType.toWireType(allocateDestructors(destructorsRef), rv);\n" + "}));\n"; args1.push(invokerFnBody); diff --git a/src/headlessCanvas.js b/src/headlessCanvas.js index 6b0f9d47..4bd17a7b 100644 --- a/src/headlessCanvas.js +++ b/src/headlessCanvas.js @@ -446,6 +446,7 @@ function headlessCanvas() { 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; + case /* GL_ARRAY_BUFFER_BINDING */ 0x8894: return 0; default: console.log('getParameter ' + pname + '?'); return 0; } }, diff --git a/src/jsifier.js b/src/jsifier.js index f4819584..35846d39 100644 --- a/src/jsifier.js +++ b/src/jsifier.js @@ -224,6 +224,7 @@ function JSify(data, functionsOnly) { // globalVariable function globalVariableHandler(item) { + function needsPostSet(value) { if (typeof value !== 'string') return false; // (' is ok, as it is something we can indexize later into a concrete int: ('{{ FI_ ... @@ -274,7 +275,9 @@ function JSify(data, functionsOnly) { constant = Runtime.alignMemory(calcAllocatedSize(item.type)); } else { if (item.external) { - if (Runtime.isNumberType(item.type) || isPointerType(item.type)) { + if (LibraryManager.library[item.ident.slice(1)]) { + constant = LibraryManager.library[item.ident.slice(1)]; + } else if (Runtime.isNumberType(item.type) || isPointerType(item.type)) { constant = zeros(Runtime.getNativeFieldSize(item.type)); } else { constant = makeEmptyStruct(item.type); @@ -282,22 +285,23 @@ function JSify(data, functionsOnly) { } else { constant = parseConst(item.value, item.type, item.ident); } - assert(typeof constant === 'object');//, [typeof constant, JSON.stringify(constant), item.external]); // This is a flattened object. We need to find its idents, so they can be assigned to later - var structTypes = null; - constant.forEach(function(value, i) { - if (needsPostSet(value)) { // ident, or expression containing an ident - if (!structTypes) structTypes = generateStructTypes(item.type); - itemsDict.GlobalVariablePostSet.push({ - intertype: 'GlobalVariablePostSet', - JS: makeSetValue(makeGlobalUse(item.ident), i, value, structTypes[i], false, true) + ';' // ignore=true, since e.g. rtti and statics cause lots of safe_heap errors - }); - constant[i] = '0'; - } else { - if (typeof value === 'string') constant[i] = deParenCarefully(value); - } - }); + if (typeof constant === 'object') { + var structTypes = null; + constant.forEach(function(value, i) { + if (needsPostSet(value)) { // ident, or expression containing an ident + if (!structTypes) structTypes = generateStructTypes(item.type); + itemsDict.GlobalVariablePostSet.push({ + intertype: 'GlobalVariablePostSet', + JS: makeSetValue(makeGlobalUse(item.ident), i, value, structTypes[i], false, true) + ';' // ignore=true, since e.g. rtti and statics cause lots of safe_heap errors + }); + constant[i] = '0'; + } else { + if (typeof value === 'string') constant[i] = deParenCarefully(value); + } + }); + } if (item.external) { // External variables in shared libraries should not be declared as @@ -312,14 +316,18 @@ function JSify(data, functionsOnly) { } // ensure alignment - var extra = Runtime.alignMemory(constant.length) - constant.length; - if (item.ident.substr(0, 5) == '__ZTV') extra += Runtime.alignMemory(QUANTUM_SIZE); - while (extra-- > 0) constant.push(0); + if (typeof constant === 'object') { + var extra = Runtime.alignMemory(constant.length) - constant.length; + if (item.ident.substr(0, 5) == '__ZTV') extra += Runtime.alignMemory(QUANTUM_SIZE); + while (extra-- > 0) constant.push(0); + } } // NOTE: This is the only place that could potentially create static // allocations in a shared library. - constant = makePointer(constant, null, allocator, item.type, index); + if (typeof constant !== 'string') { + constant = makePointer(constant, null, allocator, item.type, index); + } var js = (index !== null ? '' : item.ident + '=') + constant; if (js) js += ';'; @@ -387,6 +395,13 @@ function JSify(data, functionsOnly) { // Note: We don't return the dependencies here. Be careful not to end up where this matters if (('_' + ident) in Functions.implementedFunctions) return ''; + if (!LibraryManager.library.hasOwnProperty(ident) && !LibraryManager.library.hasOwnProperty(ident + '__inline')) { + if (ERROR_ON_UNDEFINED_SYMBOLS) error('unresolved symbol: ' + ident); + else if (VERBOSE || (WARN_ON_UNDEFINED_SYMBOLS && !LINKABLE)) warn('unresolved symbol: ' + ident); + // emit a stub that will fail at runtime + LibraryManager.library[shortident] = new Function("Module['printErr']('missing function: " + shortident + "'); abort(-1);"); + } + var snippet = LibraryManager.library[ident]; var redirectedIdent = null; var deps = LibraryManager.library[ident + '__deps'] || []; @@ -448,7 +463,7 @@ function JSify(data, functionsOnly) { } else { ident = '_' + ident; } - if (VERBOSE) printErr('adding ' + ident + ' and deps ' + deps); + if (VERBOSE) printErr('adding ' + ident + ' and deps ' + deps + ' : ' + (snippet + '').substr(0, 40)); var depsText = (deps ? '\n' + deps.map(addFromLibrary).filter(function(x) { return x != '' }).join('\n') : ''); var contentText = isFunction ? snippet : ('var ' + ident + '=' + snippet + ';'); if (ASM_JS) { @@ -478,7 +493,6 @@ function JSify(data, functionsOnly) { item.JS = ''; } else { // If this is not linkable, anything not in the library is definitely missing - var cancel = false; if (item.ident in DEAD_FUNCTIONS) { if (LibraryManager.library[shortident + '__asm']) { warn('cannot kill asm library function ' + item.ident); @@ -488,17 +502,7 @@ function JSify(data, functionsOnly) { delete LibraryManager.library[shortident + '__deps']; } } - if (!LINKABLE && !LibraryManager.library.hasOwnProperty(shortident) && !LibraryManager.library.hasOwnProperty(shortident + '__inline')) { - if (ERROR_ON_UNDEFINED_SYMBOLS) error('unresolved symbol: ' + shortident); - else if (VERBOSE || WARN_ON_UNDEFINED_SYMBOLS) warn('unresolved symbol: ' + shortident); - if (ASM_JS) { - // emit a stub that will fail during runtime. this allows asm validation to succeed. - LibraryManager.library[shortident] = new Function("Module['printErr']('missing function: " + shortident + "'); abort(-1);"); - } else { - cancel = true; // emit nothing, not even var X = undefined; - } - } - item.JS = cancel ? ';' : addFromLibrary(shortident); + item.JS = addFromLibrary(shortident); } } @@ -1223,7 +1227,7 @@ function JSify(data, functionsOnly) { // in an assignment var disabled = DISABLE_EXCEPTION_CATCHING == 2 && !(item.funcData.ident in EXCEPTION_CATCHING_WHITELIST); var phiSets = calcPhiSets(item); - var call_ = makeFunctionCall(item, item.params, item.funcData, item.type, ASM_JS && !disabled, !!item.assignTo || !item.standalone, true); + var call_ = makeFunctionCall(item, item.params, item.funcData, item.type, ASM_JS && !disabled, !!item.assignTo || !item.standalone, !disabled); var ret; @@ -1844,7 +1848,7 @@ function JSify(data, functionsOnly) { // rest of the output that we started to print out earlier (see comment on the // "Final shape that will be created"). if (PRECISE_I64_MATH && Types.preciseI64MathUsed) { - if (!INCLUDE_FULL_LIBRARY && !SIDE_MODULE) { + if (!INCLUDE_FULL_LIBRARY && !SIDE_MODULE && !BUILD_AS_SHARED_LIB) { // first row are utilities called from generated code, second are needed from fastLong ['i64Add', 'i64Subtract', 'bitshift64Shl', 'bitshift64Lshr', 'bitshift64Ashr', 'llvm_ctlz_i32', 'llvm_cttz_i32'].forEach(function(func) { diff --git a/src/library.js b/src/library.js index bca47944..f69b52e5 100644 --- a/src/library.js +++ b/src/library.js @@ -52,32 +52,33 @@ LibraryManager.library = { ___setErrNo(ERRNO_CODES.ENOTDIR); return 0; } - var err = _open(dirname, {{{ cDefine('O_RDONLY') }}}, allocate([0, 0, 0, 0], 'i32', ALLOC_STACK)); - // open returns 0 on failure, not -1 - return err === -1 ? 0 : err; + var fd = _open(dirname, {{{ cDefine('O_RDONLY') }}}, allocate([0, 0, 0, 0], 'i32', ALLOC_STACK)); + return fd === -1 ? 0 : FS.getPtrForStream(FS.getStream(fd)); }, - closedir__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', 'close'], + closedir__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', 'close', 'fileno'], closedir: function(dirp) { // int closedir(DIR *dirp); // http://pubs.opengroup.org/onlinepubs/007908799/xsh/closedir.html - return _close(dirp); + var fd = _fileno(dirp); + return _close(fd); }, telldir__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], telldir: function(dirp) { // long int telldir(DIR *dirp); // http://pubs.opengroup.org/onlinepubs/007908799/xsh/telldir.html - var stream = FS.getStream(dirp); + var stream = FS.getStreamFromPtr(dirp); if (!stream || !FS.isDir(stream.node.mode)) { ___setErrNo(ERRNO_CODES.EBADF); return -1; } return stream.position; }, - seekdir__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', 'lseek'], + seekdir__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', 'lseek', 'fileno'], seekdir: function(dirp, loc) { // void seekdir(DIR *dirp, long int loc); // http://pubs.opengroup.org/onlinepubs/007908799/xsh/seekdir.html - _lseek(dirp, loc, {{{ cDefine('SEEK_SET') }}}); + var fd = _fileno(dirp); + _lseek(fd, loc, {{{ cDefine('SEEK_SET') }}}); }, rewinddir__deps: ['seekdir'], rewinddir: function(dirp) { @@ -89,7 +90,7 @@ LibraryManager.library = { readdir_r: function(dirp, entry, result) { // int readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result); // http://pubs.opengroup.org/onlinepubs/007908799/xsh/readdir_r.html - var stream = FS.getStream(dirp); + var stream = FS.getStreamFromPtr(dirp); if (!stream) { return ___setErrNo(ERRNO_CODES.EBADF); } @@ -134,7 +135,7 @@ LibraryManager.library = { readdir: function(dirp) { // struct dirent *readdir(DIR *dirp); // http://pubs.opengroup.org/onlinepubs/007908799/xsh/readdir_r.html - var stream = FS.getStream(dirp); + var stream = FS.getStreamFromPtr(dirp); if (!stream) { ___setErrNo(ERRNO_CODES.EBADF); return 0; @@ -1641,6 +1642,7 @@ LibraryManager.library = { for (var i = 0; i < maxx; i++) { next = get(); {{{ makeSetValue('argPtr++', 0, 'next', 'i8') }}}; + if (next === 0) return i > 0 ? fields : fields-1; // we failed to read the full length of this field } formatIndex += nextC - formatIndex + 1; continue; @@ -2289,19 +2291,20 @@ LibraryManager.library = { clearerr: function(stream) { // void clearerr(FILE *stream); // http://pubs.opengroup.org/onlinepubs/000095399/functions/clearerr.html - stream = FS.getStream(stream); + stream = FS.getStreamFromPtr(stream); if (!stream) { return; } stream.eof = false; stream.error = false; }, - fclose__deps: ['close', 'fsync'], + fclose__deps: ['close', 'fsync', 'fileno'], fclose: function(stream) { // int fclose(FILE *stream); // http://pubs.opengroup.org/onlinepubs/000095399/functions/fclose.html - _fsync(stream); - return _close(stream); + var fd = _fileno(stream); + _fsync(fd); + return _close(fd); }, fdopen__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], fdopen: function(fildes, mode) { @@ -2322,21 +2325,21 @@ LibraryManager.library = { } else { stream.error = false; stream.eof = false; - return fildes; + return FS.getPtrForStream(stream); } }, feof__deps: ['$FS'], feof: function(stream) { // int feof(FILE *stream); // http://pubs.opengroup.org/onlinepubs/000095399/functions/feof.html - stream = FS.getStream(stream); + stream = FS.getStreamFromPtr(stream); return Number(stream && stream.eof); }, ferror__deps: ['$FS'], ferror: function(stream) { // int ferror(FILE *stream); // http://pubs.opengroup.org/onlinepubs/000095399/functions/ferror.html - stream = FS.getStream(stream); + stream = FS.getStreamFromPtr(stream); return Number(stream && stream.error); }, fflush__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], @@ -2350,7 +2353,7 @@ LibraryManager.library = { fgetc: function(stream) { // int fgetc(FILE *stream); // http://pubs.opengroup.org/onlinepubs/000095399/functions/fgetc.html - var streamObj = FS.getStream(stream); + var streamObj = FS.getStreamFromPtr(stream); if (!streamObj) return -1; if (streamObj.eof || streamObj.error) return -1; var ret = _fread(_fgetc.ret, 1, 1, stream); @@ -2375,7 +2378,7 @@ LibraryManager.library = { fgetpos: function(stream, pos) { // int fgetpos(FILE *restrict stream, fpos_t *restrict pos); // http://pubs.opengroup.org/onlinepubs/000095399/functions/fgetpos.html - stream = FS.getStream(stream); + stream = FS.getStreamFromPtr(stream); if (!stream) { ___setErrNo(ERRNO_CODES.EBADF); return -1; @@ -2393,7 +2396,7 @@ LibraryManager.library = { fgets: function(s, n, stream) { // char *fgets(char *restrict s, int n, FILE *restrict stream); // http://pubs.opengroup.org/onlinepubs/000095399/functions/fgets.html - var streamObj = FS.getStream(stream); + var streamObj = FS.getStreamFromPtr(stream); if (!streamObj) return 0; if (streamObj.error || streamObj.eof) return 0; var byte_; @@ -2417,8 +2420,7 @@ LibraryManager.library = { fileno: function(stream) { // int fileno(FILE *stream); // http://pubs.opengroup.org/onlinepubs/000095399/functions/fileno.html - // We use file descriptor numbers and FILE* streams interchangeably. - return stream; + return FS.getStreamFromPtr(stream).fd; }, ftrylockfile: function() { // int ftrylockfile(FILE *file); @@ -2460,19 +2462,20 @@ LibraryManager.library = { ___setErrNo(ERRNO_CODES.EINVAL); return 0; } - var ret = _open(filename, flags, allocate([0x1FF, 0, 0, 0], 'i32', ALLOC_STACK)); // All creation permissions. - return (ret == -1) ? 0 : ret; + var fd = _open(filename, flags, allocate([0x1FF, 0, 0, 0], 'i32', ALLOC_STACK)); // All creation permissions. + return fd === -1 ? 0 : FS.getPtrForStream(FS.getStream(fd)); }, - fputc__deps: ['$FS', 'write'], + fputc__deps: ['$FS', 'write', 'fileno'], fputc__postset: '_fputc.ret = allocate([0], "i8", ALLOC_STATIC);', fputc: function(c, stream) { // int fputc(int c, FILE *stream); // http://pubs.opengroup.org/onlinepubs/000095399/functions/fputc.html var chr = unSign(c & 0xFF); {{{ makeSetValue('_fputc.ret', '0', 'chr', 'i8') }}}; - var ret = _write(stream, _fputc.ret, 1); + var fd = _fileno(stream); + var ret = _write(fd, _fputc.ret, 1); if (ret == -1) { - var streamObj = FS.getStream(stream); + var streamObj = FS.getStreamFromPtr(stream); if (streamObj) streamObj.error = true; return -1; } else { @@ -2488,11 +2491,12 @@ LibraryManager.library = { return _fputc(c, {{{ makeGetValue(makeGlobalUse('_stdout'), '0', 'void*') }}}); }, putchar_unlocked: 'putchar', - fputs__deps: ['write', 'strlen'], + fputs__deps: ['write', 'strlen', 'fileno'], fputs: function(s, stream) { // int fputs(const char *restrict s, FILE *restrict stream); // http://pubs.opengroup.org/onlinepubs/000095399/functions/fputs.html - return _write(stream, s, _strlen(s)); + var fd = _fileno(stream); + return _write(fd, s, _strlen(s)); }, puts__deps: ['fputs', 'fputc', 'stdout'], puts: function(s) { @@ -2517,7 +2521,7 @@ LibraryManager.library = { return 0; } var bytesRead = 0; - var streamObj = FS.getStream(stream); + var streamObj = FS.getStreamFromPtr(stream); if (!streamObj) { ___setErrNo(ERRNO_CODES.EBADF); return 0; @@ -2527,7 +2531,7 @@ LibraryManager.library = { bytesToRead--; bytesRead++; } - var err = _read(stream, ptr, bytesToRead); + var err = _read(streamObj.fd, ptr, bytesToRead); if (err == -1) { if (streamObj) streamObj.error = true; return 0; @@ -2541,7 +2545,7 @@ LibraryManager.library = { // FILE *freopen(const char *restrict filename, const char *restrict mode, FILE *restrict stream); // http://pubs.opengroup.org/onlinepubs/000095399/functions/freopen.html if (!filename) { - var streamObj = FS.getStream(stream); + var streamObj = FS.getStreamFromPtr(stream); if (!streamObj) { ___setErrNo(ERRNO_CODES.EBADF); return 0; @@ -2553,15 +2557,16 @@ LibraryManager.library = { _fclose(stream); return _fopen(filename, mode); }, - fseek__deps: ['$FS', 'lseek'], + fseek__deps: ['$FS', 'lseek', 'fileno'], fseek: function(stream, offset, whence) { // int fseek(FILE *stream, long offset, int whence); // http://pubs.opengroup.org/onlinepubs/000095399/functions/fseek.html - var ret = _lseek(stream, offset, whence); + var fd = _fileno(stream); + var ret = _lseek(fd, offset, whence); if (ret == -1) { return -1; } - stream = FS.getStream(stream); + stream = FS.getStreamFromPtr(stream); stream.eof = false; return 0; }, @@ -2570,7 +2575,7 @@ LibraryManager.library = { fsetpos: function(stream, pos) { // int fsetpos(FILE *stream, const fpos_t *pos); // http://pubs.opengroup.org/onlinepubs/000095399/functions/fsetpos.html - stream = FS.getStream(stream); + stream = FS.getStreamFromPtr(stream); if (!stream) { ___setErrNo(ERRNO_CODES.EBADF); return -1; @@ -2589,7 +2594,7 @@ LibraryManager.library = { ftell: function(stream) { // long ftell(FILE *stream); // http://pubs.opengroup.org/onlinepubs/000095399/functions/ftell.html - stream = FS.getStream(stream); + stream = FS.getStreamFromPtr(stream); if (!stream) { ___setErrNo(ERRNO_CODES.EBADF); return -1; @@ -2602,15 +2607,16 @@ LibraryManager.library = { } }, ftello: 'ftell', - fwrite__deps: ['$FS', 'write'], + fwrite__deps: ['$FS', 'write', 'fileno'], fwrite: function(ptr, size, nitems, stream) { // size_t fwrite(const void *restrict ptr, size_t size, size_t nitems, FILE *restrict stream); // http://pubs.opengroup.org/onlinepubs/000095399/functions/fwrite.html var bytesToWrite = nitems * size; if (bytesToWrite == 0) return 0; - var bytesWritten = _write(stream, ptr, bytesToWrite); + var fd = _fileno(stream); + var bytesWritten = _write(fd, ptr, bytesToWrite); if (bytesWritten == -1) { - var streamObj = FS.getStream(stream); + var streamObj = FS.getStreamFromPtr(stream); if (streamObj) streamObj.error = true; return 0; } else { @@ -2673,7 +2679,7 @@ LibraryManager.library = { // void rewind(FILE *stream); // http://pubs.opengroup.org/onlinepubs/000095399/functions/rewind.html _fseek(stream, 0, 0); // SEEK_SET. - var streamObj = FS.getStream(stream); + var streamObj = FS.getStreamFromPtr(stream); if (streamObj) streamObj.error = false; }, setvbuf: function(stream, buf, type, size) { @@ -2730,7 +2736,7 @@ LibraryManager.library = { ungetc: function(c, stream) { // int ungetc(int c, FILE *stream); // http://pubs.opengroup.org/onlinepubs/000095399/functions/ungetc.html - stream = FS.getStream(stream); + stream = FS.getStreamFromPtr(stream); if (!stream) { return -1; } @@ -2755,7 +2761,7 @@ LibraryManager.library = { fscanf: function(stream, format, varargs) { // int fscanf(FILE *restrict stream, const char *restrict format, ... ); // http://pubs.opengroup.org/onlinepubs/000095399/functions/scanf.html - var streamObj = FS.getStream(stream); + var streamObj = FS.getStreamFromPtr(stream); if (!streamObj) { return -1; } @@ -2898,7 +2904,7 @@ LibraryManager.library = { // ========================================================================== mmap__deps: ['$FS'], - mmap: function(start, num, prot, flags, stream, offset) { + mmap: function(start, num, prot, flags, fd, offset) { /* FIXME: Since mmap is normally implemented at the kernel level, * this implementation simply uses malloc underneath the call to * mmap. @@ -2909,13 +2915,13 @@ LibraryManager.library = { if (!_mmap.mappings) _mmap.mappings = {}; - if (stream == -1) { + if (fd == -1) { ptr = _malloc(num); if (!ptr) return -1; _memset(ptr, 0, num); allocated = true; } else { - var info = FS.getStream(stream); + var info = FS.getStream(fd); if (!info) return -1; try { var res = FS.mmap(info, HEAPU8, start, num, offset, prot, flags); @@ -3229,22 +3235,34 @@ LibraryManager.library = { strtoll: function(str, endptr, base) { return __parseInt64(str, endptr, base, '-9223372036854775808', '9223372036854775807'); // LLONG_MIN, LLONG_MAX. }, - strtoll_l: 'strtoll', // no locale support yet + strtoll_l__deps: ['strtoll'], + strtoll_l: function(str, endptr, base) { + return _strtoll(str, endptr, base); // no locale support yet + }, strtol__deps: ['_parseInt'], strtol: function(str, endptr, base) { return __parseInt(str, endptr, base, -2147483648, 2147483647, 32); // LONG_MIN, LONG_MAX. }, - strtol_l: 'strtol', // no locale support yet + strtol_l__deps: ['strtol'], + strtol_l: function(str, endptr, base) { + return _strtol(str, endptr, base); // no locale support yet + }, strtoul__deps: ['_parseInt'], strtoul: function(str, endptr, base) { return __parseInt(str, endptr, base, 0, 4294967295, 32, true); // ULONG_MAX. }, - strtoul_l: 'strtoul', // no locale support yet + strtoul_l__deps: ['strtoul'], + strtoul_l: function(str, endptr, base) { + return _strtoul(str, endptr, base); // no locale support yet + }, strtoull__deps: ['_parseInt64'], strtoull: function(str, endptr, base) { return __parseInt64(str, endptr, base, 0, '18446744073709551615', true); // ULONG_MAX. }, - strtoull_l: 'strtoull', // no locale support yet + strtoull_l__deps: ['strtoull'], + strtoull_l: function(str, endptr, base) { + return _strtoull(str, endptr, base); // no locale support yet + }, atoi__deps: ['strtol'], atoi: function(ptr) { @@ -3435,13 +3453,25 @@ LibraryManager.library = { return limit; }, - // Use browser's Math.random(). We can't set a seed, though. - srand: function(seed) {}, // XXX ignored - rand: function() { - return Math.floor(Math.random()*0x80000000); + __rand_seed: 'allocate([0x0273459b, 0, 0, 0], "i32", ALLOC_STATIC)', + srand__deps: ['__rand_seed'], + srand: function(seed) { + {{{ makeSetValue('___rand_seed', 0, 'seed', 'i32') }}} + }, + rand_r__sig: 'ii', + rand_r__asm: true, + rand_r: function(seedp) { + seedp = seedp|0; + var val = 0; + val = ((Math_imul({{{ makeGetValueAsm('seedp', 0, 'i32') }}}, 31010991)|0) + 0x676e6177 ) & {{{ cDefine('RAND_MAX') }}}; // assumes RAND_MAX is in bit mask form (power of 2 minus 1) + {{{ makeSetValueAsm('seedp', 0, 'val', 'i32') }}}; + return val|0; }, - rand_r: function(seed) { // XXX ignores the seed - return Math.floor(Math.random()*0x80000000); + rand__sig: 'i', + rand__asm: true, + rand__deps: ['rand_r', '__rand_seed'], + rand: function() { + return _rand_r(___rand_seed)|0; }, drand48: function() { @@ -3485,11 +3515,18 @@ LibraryManager.library = { return ret; }, + emscripten_memcpy_big: function(dest, src, num) { + HEAPU8.set(HEAPU8.subarray(src, src+num), dest); + return dest; + }, + memcpy__asm: true, memcpy__sig: 'iiii', + memcpy__deps: ['emscripten_memcpy_big'], memcpy: function(dest, src, num) { dest = dest|0; src = src|0; num = num|0; var ret = 0; + if ((num|0) >= 4096) return _emscripten_memcpy_big(dest|0, src|0, num|0)|0; ret = dest|0; if ((dest&3) == (src&3)) { while (dest & 3) { @@ -3729,76 +3766,6 @@ LibraryManager.library = { return pdest; }, - strcmp__deps: ['strncmp'], - strcmp: function(px, py) { - return _strncmp(px, py, TOTAL_MEMORY); - }, - // We always assume ASCII locale. - strcoll: 'strcmp', - strcoll_l: 'strcmp', - - strcasecmp__asm: true, - strcasecmp__sig: 'iii', - strcasecmp__deps: ['strncasecmp'], - strcasecmp: function(px, py) { - px = px|0; py = py|0; - return _strncasecmp(px, py, -1)|0; - }, - - strncmp: function(px, py, n) { - var i = 0; - while (i < n) { - var x = {{{ makeGetValue('px', 'i', 'i8', 0, 1) }}}; - var y = {{{ makeGetValue('py', 'i', 'i8', 0, 1) }}}; - if (x == y && x == 0) return 0; - if (x == 0) return -1; - if (y == 0) return 1; - if (x == y) { - i ++; - continue; - } else { - return x > y ? 1 : -1; - } - } - return 0; - }, - - strncasecmp__asm: true, - strncasecmp__sig: 'iiii', - strncasecmp__deps: ['tolower'], - strncasecmp: function(px, py, n) { - px = px|0; py = py|0; n = n|0; - var i = 0, x = 0, y = 0; - while ((i>>>0) < (n>>>0)) { - x = _tolower({{{ makeGetValueAsm('px', 'i', 'i8', 0, 1) }}})|0; - y = _tolower({{{ makeGetValueAsm('py', 'i', 'i8', 0, 1) }}})|0; - if (((x|0) == (y|0)) & ((x|0) == 0)) return 0; - if ((x|0) == 0) return -1; - if ((y|0) == 0) return 1; - if ((x|0) == (y|0)) { - i = (i + 1)|0; - continue; - } else { - return ((x>>>0) > (y>>>0) ? 1 : -1)|0; - } - } - return 0; - }, - - memcmp__asm: true, - memcmp__sig: 'iiii', - memcmp: function(p1, p2, num) { - p1 = p1|0; p2 = p2|0; num = num|0; - var i = 0, v1 = 0, v2 = 0; - while ((i|0) < (num|0)) { - v1 = {{{ makeGetValueAsm('p1', 'i', 'i8', true) }}}; - v2 = {{{ makeGetValueAsm('p2', 'i', 'i8', true) }}}; - if ((v1|0) != (v2|0)) return ((v1|0) > (v2|0) ? 1 : -1)|0; - i = (i+1)|0; - } - return 0; - }, - memchr: function(ptr, chr, num) { chr = unSign(chr); for (var i = 0; i < num; i++) { @@ -4000,7 +3967,10 @@ LibraryManager.library = { } }, _toupper: 'toupper', - toupper_l: 'toupper', + toupper_l__deps: ['toupper'], + toupper_l: function(str, endptr, base) { + return _toupper(str, endptr, base); // no locale support yet + }, tolower__asm: true, tolower__sig: 'ii', @@ -4011,65 +3981,104 @@ LibraryManager.library = { return (chr - {{{ charCode('A') }}} + {{{ charCode('a') }}})|0; }, _tolower: 'tolower', - tolower_l: 'tolower', + tolower_l__deps: ['tolower'], + tolower_l: function(chr) { + return _tolower(chr); // no locale support yet + }, // The following functions are defined as macros in glibc. islower: function(chr) { return chr >= {{{ charCode('a') }}} && chr <= {{{ charCode('z') }}}; }, - islower_l: 'islower', + islower_l__deps: ['islower'], + islower_l: function(chr) { + return _islower(chr); // no locale support yet + }, isupper: function(chr) { return chr >= {{{ charCode('A') }}} && chr <= {{{ charCode('Z') }}}; }, - isupper_l: 'isupper', + isupper_l__deps: ['isupper'], + isupper_l: function(chr) { + return _isupper(chr); // no locale support yet + }, isalpha: function(chr) { return (chr >= {{{ charCode('a') }}} && chr <= {{{ charCode('z') }}}) || (chr >= {{{ charCode('A') }}} && chr <= {{{ charCode('Z') }}}); }, - isalpha_l: 'isalpha', + isalpha_l__deps: ['isalpha'], + isalpha_l: function(chr) { + return _isalpha(chr); // no locale support yet + }, isdigit: function(chr) { return chr >= {{{ charCode('0') }}} && chr <= {{{ charCode('9') }}}; }, - isdigit_l: 'isdigit', + isdigit_l__deps: ['isdigit'], + isdigit_l: function(chr) { + return _isdigit(chr); // no locale support yet + }, isxdigit: function(chr) { return (chr >= {{{ charCode('0') }}} && chr <= {{{ charCode('9') }}}) || (chr >= {{{ charCode('a') }}} && chr <= {{{ charCode('f') }}}) || (chr >= {{{ charCode('A') }}} && chr <= {{{ charCode('F') }}}); }, - isxdigit_l: 'isxdigit', + isxdigit_l__deps: ['isxdigit'], + isxdigit_l: function(chr) { + return _isxdigit(chr); // no locale support yet + }, isalnum: function(chr) { return (chr >= {{{ charCode('0') }}} && chr <= {{{ charCode('9') }}}) || (chr >= {{{ charCode('a') }}} && chr <= {{{ charCode('z') }}}) || (chr >= {{{ charCode('A') }}} && chr <= {{{ charCode('Z') }}}); }, - isalnum_l: 'isalnum', + isalnum_l__deps: ['isalnum'], + isalnum_l: function(chr) { + return _isalnum(chr); // no locale support yet + }, ispunct: function(chr) { return (chr >= {{{ charCode('!') }}} && chr <= {{{ charCode('/') }}}) || (chr >= {{{ charCode(':') }}} && chr <= {{{ charCode('@') }}}) || (chr >= {{{ charCode('[') }}} && chr <= {{{ charCode('`') }}}) || (chr >= {{{ charCode('{') }}} && chr <= {{{ charCode('~') }}}); }, - ispunct_l: 'ispunct', + ispunct_l__deps: ['ispunct'], + ispunct_l: function(chr) { + return _ispunct(chr); // no locale support yet + }, isspace: function(chr) { return (chr == 32) || (chr >= 9 && chr <= 13); }, - isspace_l: 'isspace', + isspace_l__deps: ['isspace'], + isspace_l: function(chr) { + return _isspace(chr); // no locale support yet + }, isblank: function(chr) { return chr == {{{ charCode(' ') }}} || chr == {{{ charCode('\t') }}}; }, - isblank_l: 'isblank', + isblank_l__deps: ['isblank'], + isblank_l: function(chr) { + return _isblank(chr); // no locale support yet + }, iscntrl: function(chr) { return (0 <= chr && chr <= 0x1F) || chr === 0x7F; }, - iscntrl_l: 'iscntrl', + iscntrl_l__deps: ['iscntrl'], + iscntrl_l: function(chr) { + return _iscntrl(chr); // no locale support yet + }, isprint: function(chr) { return 0x1F < chr && chr < 0x7F; }, - isprint_l: 'isprint', + isprint_l__deps: ['isprint'], + isprint_l: function(chr) { + return _isprint(chr); // no locale support yet + }, isgraph: function(chr) { return 0x20 < chr && chr < 0x7F; }, - isgraph_l: 'isgraph', + isgraph_l__deps: ['isgraph'], + isgraph_l: function(chr) { + return _isgraph(chr); // no locale support yet + }, // Lookup tables for glibc ctype implementation. __ctype_b_loc: function() { // http://refspecs.freestandards.org/LSB_3.0.0/LSB-Core-generic/LSB-Core-generic/baselib---ctype-b-loc.html @@ -4415,7 +4424,7 @@ LibraryManager.library = { // and free the exception. Note that if the dynCall on the destructor fails // due to calling apply on undefined, that means that the destructor is // an invalid index into the FUNCTION_TABLE, so something has gone wrong. - __cxa_end_catch__deps: ['__cxa_free_exception', '__cxa_last_thrown_exception', '___cxa_exception_header_size', '__cxa_caught_exceptions'], + __cxa_end_catch__deps: ['__cxa_free_exception', '__cxa_last_thrown_exception', '__cxa_exception_header_size', '__cxa_caught_exceptions'], __cxa_end_catch: function() { if (___cxa_end_catch.rethrown) { ___cxa_end_catch.rethrown = false; @@ -4609,6 +4618,8 @@ LibraryManager.library = { _ZTIv: [0], // void _ZTIPv: [0], // void* + _ZTISt9exception: 'allocate([allocate([1,0,0,0,0,0,0], "i8", ALLOC_STATIC)+8, 0], "i32", ALLOC_STATIC)', // typeinfo for std::exception + llvm_uadd_with_overflow_i8: function(x, y) { x = x & 0xff; y = y & 0xff; @@ -5887,7 +5898,10 @@ LibraryManager.library = { writeArrayToMemory(bytes, s); return bytes.length-1; }, - strftime_l: 'strftime', // no locale support yet + strftime_l__deps: ['strftime'], + strftime_l: function(s, maxsize, format, tm) { + return _strftime(s, maxsize, format, tm); // no locale support yet + }, strptime__deps: ['_isLeapYear', '_arraySum', '_addDays', '_MONTH_DAYS_REGULAR', '_MONTH_DAYS_LEAP'], strptime: function(buf, format, tm) { @@ -6129,7 +6143,10 @@ LibraryManager.library = { return 0; }, - strptime_l: 'strptime', // no locale support yet + strptime_l__deps: ['strptime'], + strptime_l: function(buf, format, tm) { + return _strptime(buf, format, tm); // no locale support yet + }, getdate: function(string) { // struct tm *getdate(const char *string); @@ -7342,20 +7359,10 @@ LibraryManager.library = { // netinet/in.h // ========================================================================== - _in6addr_any: + in6addr_any: 'allocate([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], "i8", ALLOC_STATIC)', - _in6addr_loopback: + in6addr_loopback: 'allocate([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1], "i8", ALLOC_STATIC)', - _in6addr_linklocal_allnodes: - 'allocate([255,2,0,0,0,0,0,0,0,0,0,0,0,0,0,1], "i8", ALLOC_STATIC)', - _in6addr_linklocal_allrouters: - 'allocate([255,2,0,0,0,0,0,0,0,0,0,0,0,0,0,2], "i8", ALLOC_STATIC)', - _in6addr_interfacelocal_allnodes: - 'allocate([255,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1], "i8", ALLOC_STATIC)', - _in6addr_interfacelocal_allrouters: - 'allocate([255,1,0,0,0,0,0,0,0,0,0,0,0,0,0,2], "i8", ALLOC_STATIC)', - _in6addr_sitelocal_allrouters: - 'allocate([255,5,0,0,0,0,0,0,0,0,0,0,0,0,0,2], "i8", ALLOC_STATIC)', // ========================================================================== // netdb.h @@ -7510,6 +7517,7 @@ LibraryManager.library = { } else { {{{ makeSetValue('ai', C_STRUCTS.addrinfo.ai_addrlen, C_STRUCTS.sockaddr_in.__size__, 'i32') }}}; } + {{{ makeSetValue('ai', C_STRUCTS.addrinfo.ai_next, '0', 'i32') }}}; return ai; } @@ -8636,6 +8644,8 @@ LibraryManager.library = { return 0; }, + mkport: function() { throw 'TODO' }, + // ========================================================================== // select.h // ========================================================================== diff --git a/src/library_browser.js b/src/library_browser.js index 458a8dd2..50a8fc6f 100644 --- a/src/library_browser.js +++ b/src/library_browser.js @@ -13,6 +13,7 @@ mergeInto(LibraryManager.library, { $Browser: { mainLoop: { scheduler: null, + method: '', shouldPause: false, paused: false, queue: [], @@ -445,6 +446,10 @@ mergeInto(LibraryManager.library, { 0; }, + getMouseWheelDelta: function(event) { + return Math.max(-1, Math.min(1, event.type === 'DOMMouseScroll' ? event.detail : -event.wheelDelta)); + }, + mouseX: 0, mouseY: 0, mouseMovementX: 0, @@ -674,6 +679,8 @@ mergeInto(LibraryManager.library, { }, emscripten_async_prepare: function(file, onload, onerror) { + Module['noExitRuntime'] = true; + var _file = Pointer_stringify(file); var data = FS.analyzePath(_file); if (!data.exists) return -1; @@ -693,6 +700,8 @@ mergeInto(LibraryManager.library, { }, emscripten_async_prepare_data: function(data, size, suffix, arg, onload, onerror) { + Module['noExitRuntime'] = true; + var _suffix = Pointer_stringify(suffix); if (!Browser.asyncPrepareDataCounter) Browser.asyncPrepareDataCounter = 0; var name = 'prepare_data_' + (Browser.asyncPrepareDataCounter++) + '.' + _suffix; @@ -784,6 +793,11 @@ mergeInto(LibraryManager.library, { GL.newRenderingFrameStarted(); #endif + if (Browser.mainLoop.method === 'timeout' && Module.ctx) { + Module.printErr('Looks like you are rendering without using requestAnimationFrame for the main loop. You should use 0 for the frame rate in emscripten_set_main_loop in order to use requestAnimationFrame, as that can greatly improve your frame rates!'); + Browser.mainLoop.method = ''; // just warn once per call to set main loop + } + if (Module['preMainLoop']) { Module['preMainLoop'](); } @@ -814,11 +828,13 @@ mergeInto(LibraryManager.library, { if (fps && fps > 0) { Browser.mainLoop.scheduler = function Browser_mainLoop_scheduler() { setTimeout(Browser.mainLoop.runner, 1000/fps); // doing this each time means that on exception, we stop - } + }; + Browser.mainLoop.method = 'timeout'; } else { Browser.mainLoop.scheduler = function Browser_mainLoop_scheduler() { Browser.requestAnimationFrame(Browser.mainLoop.runner); - } + }; + Browser.mainLoop.method = 'rAF'; } Browser.mainLoop.scheduler(); @@ -946,6 +962,8 @@ mergeInto(LibraryManager.library, { }, emscripten_call_worker: function(id, funcName, data, size, callback, arg) { + Module['noExitRuntime'] = true; // should we only do this if there is a callback? + funcName = Pointer_stringify(funcName); var info = Browser.workers[id]; var callbackId = -1; diff --git a/src/library_egl.js b/src/library_egl.js index 11cf8951..69dd266d 100644 --- a/src/library_egl.js +++ b/src/library_egl.js @@ -555,7 +555,7 @@ var LibraryEGL = { eglGetProcAddress__deps: ['emscripten_GetProcAddress'], eglGetProcAddress: function(name_) { - return _emscripten_GetProcAddress(Pointer_stringify(name_)); + return _emscripten_GetProcAddress(name_); }, }; diff --git a/src/library_fs.js b/src/library_fs.js index e6b060f6..e97ba588 100644 --- a/src/library_fs.js +++ b/src/library_fs.js @@ -16,7 +16,7 @@ mergeInto(LibraryManager.library, { root: null, mounts: [], devices: [null], - streams: [null], + streams: [], nextInode: 1, nameTable: null, currentPath: '/', @@ -40,7 +40,17 @@ mergeInto(LibraryManager.library, { // lookupPath: function(path, opts) { path = PATH.resolve(FS.cwd(), path); - opts = opts || { recurse_count: 0 }; + opts = opts || {}; + + var defaults = { + follow_mount: true, + recurse_count: 0 + }; + for (var key in defaults) { + if (opts[key] === undefined) { + opts[key] = defaults[key]; + } + } if (opts.recurse_count > 8) { // max recursive lookup of 8 throw new FS.ErrnoError(ERRNO_CODES.ELOOP); @@ -67,10 +77,11 @@ mergeInto(LibraryManager.library, { // jump to the mount's root node if this is a mountpoint if (FS.isMountpoint(current)) { - current = current.mount.root; + if (!islast || (islast && opts.follow_mount)) { + current = current.mounted.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) { @@ -163,28 +174,26 @@ mergeInto(LibraryManager.library, { createNode: function(parent, name, mode, rdev) { if (!FS.FSNode) { FS.FSNode = function(parent, name, mode, rdev) { + if (!parent) { + parent = this; // root node sets parent to itself + } + this.parent = parent; + this.mount = parent.mount; + this.mounted = null; this.id = FS.nextInode++; this.name = name; this.mode = mode; this.node_ops = {}; this.stream_ops = {}; this.rdev = rdev; - this.parent = null; - this.mount = null; - if (!parent) { - parent = this; // root node sets parent to itself - } - this.parent = parent; - this.mount = parent.mount; - FS.hashAddNode(this); }; + FS.FSNode.prototype = {}; + // compatibility var readMode = {{{ cDefine('S_IRUGO') }}} | {{{ cDefine('S_IXUGO') }}}; var writeMode = {{{ cDefine('S_IWUGO') }}}; - FS.FSNode.prototype = {}; - // NOTE we must use Object.defineProperties instead of individual calls to // Object.defineProperty in order to make closure compiler happy Object.defineProperties(FS.FSNode.prototype, { @@ -204,7 +213,12 @@ mergeInto(LibraryManager.library, { }, }); } - return new FS.FSNode(parent, name, mode, rdev); + + var node = new FS.FSNode(parent, name, mode, rdev); + + FS.hashAddNode(node); + + return node; }, destroyNode: function(node) { FS.hashRemoveNode(node); @@ -213,7 +227,7 @@ mergeInto(LibraryManager.library, { return node === node.parent; }, isMountpoint: function(node) { - return node.mounted; + return !!node.mounted; }, isFile: function(mode) { return (mode & {{{ cDefine('S_IFMT') }}}) === {{{ cDefine('S_IFREG') }}}; @@ -344,7 +358,7 @@ mergeInto(LibraryManager.library, { // MAX_OPEN_FDS: 4096, nextfd: function(fd_start, fd_end) { - fd_start = fd_start || 1; + fd_start = fd_start || 0; fd_end = fd_end || FS.MAX_OPEN_FDS; for (var fd = fd_start; fd <= fd_end; fd++) { if (!FS.streams[fd]) { @@ -400,6 +414,22 @@ mergeInto(LibraryManager.library, { }, // + // file pointers + // + // instead of maintaining a separate mapping from FILE* to file descriptors, + // we employ a simple trick: the pointer to a stream is its fd plus 1. This + // means that all valid streams have a valid non-zero pointer while allowing + // the fs for stdin to be the standard value of zero. + // + // + getStreamFromPtr: function(ptr) { + return FS.streams[ptr - 1]; + }, + getPtrForStream: function(stream) { + return stream ? stream.fd + 1 : 0; + }, + + // // devices // // each character device consists of a device id + stream operations. @@ -441,61 +471,131 @@ mergeInto(LibraryManager.library, { // // core // + getMounts: function(mount) { + var mounts = []; + var check = [mount]; + + while (check.length) { + var m = check.pop(); + + mounts.push(m); + + check.push.apply(check, m.mounts); + } + + return mounts; + }, syncfs: function(populate, callback) { if (typeof(populate) === 'function') { callback = populate; populate = false; } + var mounts = FS.getMounts(FS.root.mount); var completed = 0; - var total = FS.mounts.length; + function done(err) { if (err) { - return callback(err); + if (!done.errored) { + done.errored = true; + return callback(err); + } + return; } - if (++completed >= total) { + if (++completed >= mounts.length) { callback(null); } }; // sync all mounts - for (var i = 0; i < FS.mounts.length; i++) { - var mount = FS.mounts[i]; + mounts.forEach(function (mount) { if (!mount.type.syncfs) { - done(null); - continue; + return done(null); } mount.type.syncfs(mount, populate, done); - } + }); }, mount: function(type, opts, mountpoint) { - var lookup; - if (mountpoint) { - lookup = FS.lookupPath(mountpoint, { follow: false }); + var root = mountpoint === '/'; + var pseudo = !mountpoint; + var node; + + if (root && FS.root) { + throw new FS.ErrnoError(ERRNO_CODES.EBUSY); + } else if (!root && !pseudo) { + var lookup = FS.lookupPath(mountpoint, { follow_mount: false }); + mountpoint = lookup.path; // use the absolute path + node = lookup.node; + + if (FS.isMountpoint(node)) { + throw new FS.ErrnoError(ERRNO_CODES.EBUSY); + } + + if (!FS.isDir(node.mode)) { + throw new FS.ErrnoError(ERRNO_CODES.ENOTDIR); + } } + var mount = { type: type, opts: opts, mountpoint: mountpoint, - root: null + mounts: [] }; + // 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; + var mountRoot = type.mount(mount); + mountRoot.mount = mount; + mount.root = mountRoot; + + if (root) { + FS.root = mountRoot; + } else if (node) { + // set as a mountpoint + node.mounted = mount; + + // add the new mount to the current mount's children + if (node.mount) { + node.mount.mounts.push(mount); } } - // add to our cached list of mounts - FS.mounts.push(mount); - return root; + + return mountRoot; + }, + unmount: function (mountpoint) { + var lookup = FS.lookupPath(mountpoint, { follow_mount: false }); + + if (!FS.isMountpoint(lookup.node)) { + throw new FS.ErrnoError(ERRNO_CODES.EINVAL); + } + + // destroy the nodes for this mount, and all its child mounts + var node = lookup.node; + var mount = node.mounted; + var mounts = FS.getMounts(mount); + + Object.keys(FS.nameTable).forEach(function (hash) { + var current = FS.nameTable[hash]; + + while (current) { + var next = current.name_next; + + if (mounts.indexOf(current.mount) !== -1) { + FS.destroyNode(current); + } + + current = next; + } + }); + + // no longer a mountpoint + node.mounted = null; + + // remove this mount from the child mounts + var idx = node.mount.mounts.indexOf(mount); + assert(idx !== -1); + node.mount.mounts.splice(idx, 1); }, lookup: function(parent, name) { return parent.node_ops.lookup(parent, name); @@ -677,7 +777,7 @@ mergeInto(LibraryManager.library, { FS.destroyNode(node); }, readlink: function(path) { - var lookup = FS.lookupPath(path, { follow: false }); + var lookup = FS.lookupPath(path); var link = lookup.node; if (!link.node_ops.readlink) { throw new FS.ErrnoError(ERRNO_CODES.EINVAL); @@ -975,6 +1075,9 @@ mergeInto(LibraryManager.library, { opts = opts || {}; opts.flags = opts.flags || 'r'; opts.encoding = opts.encoding || 'binary'; + if (opts.encoding !== 'utf8' && opts.encoding !== 'binary') { + throw new Error('Invalid encoding type "' + opts.encoding + '"'); + } var ret; var stream = FS.open(path, opts.flags); var stat = FS.stat(path); @@ -989,8 +1092,6 @@ mergeInto(LibraryManager.library, { } } else if (opts.encoding === 'binary') { ret = buf; - } else { - throw new Error('Invalid encoding type "' + opts.encoding + '"'); } FS.close(stream); return ret; @@ -999,15 +1100,16 @@ mergeInto(LibraryManager.library, { opts = opts || {}; opts.flags = opts.flags || 'w'; opts.encoding = opts.encoding || 'utf8'; + if (opts.encoding !== 'utf8' && opts.encoding !== 'binary') { + throw new Error('Invalid encoding type "' + opts.encoding + '"'); + } var stream = FS.open(path, opts.flags, opts.mode); if (opts.encoding === 'utf8') { var utf8 = new Runtime.UTF8Processor(); var buf = new Uint8Array(utf8.processJSString(data)); - FS.write(stream, buf, 0, buf.length, 0); + FS.write(stream, buf, 0, buf.length, 0, opts.canOwn); } else if (opts.encoding === 'binary') { - FS.write(stream, data, 0, data.length, 0); - } else { - throw new Error('Invalid encoding type "' + opts.encoding + '"'); + FS.write(stream, data, 0, data.length, 0, opts.canOwn); } FS.close(stream); }, @@ -1080,16 +1182,16 @@ mergeInto(LibraryManager.library, { // 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 + ')'); + {{{ makeSetValue(makeGlobalUse('_stdin'), 0, 'FS.getPtrForStream(stdin)', 'void*') }}}; + assert(stdin.fd === 0, '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 + ')'); + {{{ makeSetValue(makeGlobalUse('_stdout'), 0, 'FS.getPtrForStream(stdout)', 'void*') }}}; + assert(stdout.fd === 1, '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 + ')'); + {{{ makeSetValue(makeGlobalUse('_stderr'), 0, 'FS.getPtrForStream(stderr)', 'void*') }}}; + assert(stderr.fd === 2, 'invalid handle for stderr (' + stderr.fd + ')'); }, ensureErrnoError: function() { if (FS.ErrnoError) return; @@ -1119,7 +1221,6 @@ mergeInto(LibraryManager.library, { FS.nameTable = new Array(4096); - FS.root = FS.createNode(null, '/', {{{ cDefine('S_IFDIR') }}} | 0777, 0); FS.mount(MEMFS, {}, '/'); FS.createDefaultDirectories(); diff --git a/src/library_gl.js b/src/library_gl.js index 0a30292a..e3442a8a 100644 --- a/src/library_gl.js +++ b/src/library_gl.js @@ -210,6 +210,7 @@ var LibraryGL = { } }, +#if LEGACY_GL_EMULATION // Find a token in a shader source string findToken: function(source, token) { function isIdentChar(ch) { @@ -238,6 +239,7 @@ var LibraryGL = { } while (true); return false; }, +#endif getSource: function(shader, count, string, length) { var source = ''; @@ -255,6 +257,7 @@ var LibraryGL = { } source += frag; } +#if LEGACY_GL_EMULATION // Let's see if we need to enable the standard derivatives extension type = GLctx.getShaderParameter(GL.shaders[shader], 0x8B4F /* GL_SHADER_TYPE */); if (type == 0x8B30 /* GL_FRAGMENT_SHADER */) { @@ -270,6 +273,7 @@ var LibraryGL = { #endif } } +#endif return source; }, @@ -879,12 +883,12 @@ var LibraryGL = { glGetTexParameterfv__sig: 'viii', glGetTexParameterfv: function(target, pname, params) { - {{{ makeSetValue('params', '0', 'Module.getTexParameter(target, pname)', 'float') }}}; + {{{ makeSetValue('params', '0', 'GLctx.getTexParameter(target, pname)', 'float') }}}; }, glGetTexParameteriv__sig: 'viii', glGetTexParameteriv: function(target, pname, params) { - {{{ makeSetValue('params', '0', 'Module.getTexParameter(target, pname)', 'i32') }}}; + {{{ makeSetValue('params', '0', 'GLctx.getTexParameter(target, pname)', 'i32') }}}; }, glTexParameterfv__sig: 'viii', @@ -1544,9 +1548,7 @@ var LibraryGL = { #endif var log = GLctx.getShaderInfoLog(GL.shaders[shader]); // Work around a bug in Chromium which causes getShaderInfoLog to return null - if (!log) { - log = ""; - } + if (!log) log = '(unknown error)'; log = log.substr(0, maxLength - 1); writeStringToMemory(log, infoLog); if (length) { @@ -1560,7 +1562,10 @@ var LibraryGL = { GL.validateGLObjectID(GL.shaders, shader, 'glGetShaderiv', 'shader'); #endif if (pname == 0x8B84) { // GL_INFO_LOG_LENGTH - {{{ makeSetValue('p', '0', 'GLctx.getShaderInfoLog(GL.shaders[shader]).length + 1', 'i32') }}}; + var log = GLctx.getShaderInfoLog(GL.shaders[shader]); + // Work around a bug in Chromium which causes getShaderInfoLog to return null + if (!log) log = '(unknown error)'; + {{{ makeSetValue('p', '0', 'log.length + 1', 'i32') }}}; } else { {{{ makeSetValue('p', '0', 'GLctx.getShaderParameter(GL.shaders[shader], pname)', 'i32') }}}; } @@ -1849,7 +1854,7 @@ var LibraryGL = { }; var glEnable = _glEnable; - _glEnable = function _glEnable(cap) { + _glEnable = _emscripten_glEnable = function _glEnable(cap) { // Clean up the renderer on any change to the rendering state. The optimization of // skipping renderer setup is aimed at the case of multiple glDraw* right after each other if (GLImmediate.lastRenderer) GLImmediate.lastRenderer.cleanup(); @@ -1874,7 +1879,7 @@ var LibraryGL = { }; var glDisable = _glDisable; - _glDisable = function _glDisable(cap) { + _glDisable = _emscripten_glDisable = function _glDisable(cap) { if (GLImmediate.lastRenderer) GLImmediate.lastRenderer.cleanup(); if (cap == 0x0B60 /* GL_FOG */) { if (GLEmulation.fogEnabled != false) { @@ -1895,7 +1900,7 @@ var LibraryGL = { } glDisable(cap); }; - _glIsEnabled = function _glIsEnabled(cap) { + _glIsEnabled = _emscripten_glIsEnabled = function _glIsEnabled(cap) { if (cap == 0x0B60 /* GL_FOG */) { return GLEmulation.fogEnabled ? 1 : 0; } else if (!(cap in validCapabilities)) { @@ -1905,7 +1910,7 @@ var LibraryGL = { }; var glGetBooleanv = _glGetBooleanv; - _glGetBooleanv = function _glGetBooleanv(pname, p) { + _glGetBooleanv = _emscripten_glGetBooleanv = function _glGetBooleanv(pname, p) { var attrib = GLEmulation.getAttributeFromCapability(pname); if (attrib !== null) { var result = GLImmediate.enabledClientAttributes[attrib]; @@ -1916,7 +1921,7 @@ var LibraryGL = { }; var glGetIntegerv = _glGetIntegerv; - _glGetIntegerv = function _glGetIntegerv(pname, params) { + _glGetIntegerv = _emscripten_glGetIntegerv = function _glGetIntegerv(pname, params) { switch (pname) { case 0x84E2: pname = GLctx.MAX_TEXTURE_IMAGE_UNITS /* fake it */; break; // GL_MAX_TEXTURE_UNITS case 0x8B4A: { // GL_MAX_VERTEX_UNIFORM_COMPONENTS_ARB @@ -1985,7 +1990,7 @@ var LibraryGL = { }; var glGetString = _glGetString; - _glGetString = function _glGetString(name_) { + _glGetString = _emscripten_glGetString = function _glGetString(name_) { if (GL.stringCache[name_]) return GL.stringCache[name_]; switch(name_) { case 0x1F03 /* GL_EXTENSIONS */: // Add various extensions that we can support @@ -2009,7 +2014,7 @@ var LibraryGL = { GL.shaderOriginalSources = {}; #endif var glCreateShader = _glCreateShader; - _glCreateShader = function _glCreateShader(shaderType) { + _glCreateShader = _emscripten_glCreateShader = function _glCreateShader(shaderType) { var id = glCreateShader(shaderType); GL.shaderInfos[id] = { type: shaderType, @@ -2026,7 +2031,7 @@ var LibraryGL = { } var glShaderSource = _glShaderSource; - _glShaderSource = function _glShaderSource(shader, count, string, length) { + _glShaderSource = _emscripten_glShaderSource = function _glShaderSource(shader, count, string, length) { var source = GL.getSource(shader, count, string, length); #if GL_DEBUG console.log("glShaderSource: Input: \n" + source); @@ -2140,7 +2145,7 @@ var LibraryGL = { }; var glCompileShader = _glCompileShader; - _glCompileShader = function _glCompileShader(shader) { + _glCompileShader = _emscripten_glCompileShader = function _glCompileShader(shader) { GLctx.compileShader(GL.shaders[shader]); #if GL_DEBUG if (!GLctx.getShaderParameter(GL.shaders[shader], GLctx.COMPILE_STATUS)) { @@ -2155,14 +2160,14 @@ var LibraryGL = { GL.programShaders = {}; var glAttachShader = _glAttachShader; - _glAttachShader = function _glAttachShader(program, shader) { + _glAttachShader = _emscripten_glAttachShader = function _glAttachShader(program, shader) { if (!GL.programShaders[program]) GL.programShaders[program] = []; GL.programShaders[program].push(shader); glAttachShader(program, shader); }; var glDetachShader = _glDetachShader; - _glDetachShader = function _glDetachShader(program, shader) { + _glDetachShader = _emscripten_glDetachShader = function _glDetachShader(program, shader) { var programShader = GL.programShaders[program]; if (!programShader) { Module.printErr('WARNING: _glDetachShader received invalid program: ' + program); @@ -2174,7 +2179,7 @@ var LibraryGL = { }; var glUseProgram = _glUseProgram; - _glUseProgram = function _glUseProgram(program) { + _glUseProgram = _emscripten_glUseProgram = function _glUseProgram(program) { #if GL_DEBUG if (GL.debug) { Module.printErr('[using program with shaders]'); @@ -2195,7 +2200,7 @@ var LibraryGL = { } var glDeleteProgram = _glDeleteProgram; - _glDeleteProgram = function _glDeleteProgram(program) { + _glDeleteProgram = _emscripten_glDeleteProgram = function _glDeleteProgram(program) { glDeleteProgram(program); if (program == GL.currProgram) { GLImmediate.currentRenderer = null; // This changes the FFP emulation shader program, need to recompute that. @@ -2206,12 +2211,12 @@ var LibraryGL = { // If attribute 0 was not bound, bind it to 0 for WebGL performance reasons. Track if 0 is free for that. var zeroUsedPrograms = {}; var glBindAttribLocation = _glBindAttribLocation; - _glBindAttribLocation = function _glBindAttribLocation(program, index, name) { + _glBindAttribLocation = _emscripten_glBindAttribLocation = function _glBindAttribLocation(program, index, name) { if (index == 0) zeroUsedPrograms[program] = true; glBindAttribLocation(program, index, name); }; var glLinkProgram = _glLinkProgram; - _glLinkProgram = function _glLinkProgram(program) { + _glLinkProgram = _emscripten_glLinkProgram = function _glLinkProgram(program) { if (!(program in zeroUsedPrograms)) { GLctx.bindAttribLocation(GL.programs[program], 0, 'a_position'); } @@ -2219,7 +2224,7 @@ var LibraryGL = { }; var glBindBuffer = _glBindBuffer; - _glBindBuffer = function _glBindBuffer(target, buffer) { + _glBindBuffer = _emscripten_glBindBuffer = function _glBindBuffer(target, buffer) { glBindBuffer(target, buffer); if (target == GLctx.ARRAY_BUFFER) { if (GLEmulation.currentVao) { @@ -2234,7 +2239,7 @@ var LibraryGL = { }; var glGetFloatv = _glGetFloatv; - _glGetFloatv = function _glGetFloatv(pname, params) { + _glGetFloatv = _emscripten_glGetFloatv = function _glGetFloatv(pname, params) { if (pname == 0x0BA6) { // GL_MODELVIEW_MATRIX HEAPF32.set(GLImmediate.matrix[0/*m*/], params >> 2); } else if (pname == 0x0BA7) { // GL_PROJECTION_MATRIX @@ -2257,7 +2262,7 @@ var LibraryGL = { }; var glHint = _glHint; - _glHint = function _glHint(target, mode) { + _glHint = _emscripten_glHint = function _glHint(target, mode) { if (target == 0x84EF) { // GL_TEXTURE_COMPRESSION_HINT return; } @@ -2265,21 +2270,21 @@ var LibraryGL = { }; var glEnableVertexAttribArray = _glEnableVertexAttribArray; - _glEnableVertexAttribArray = function _glEnableVertexAttribArray(index) { + _glEnableVertexAttribArray = _emscripten_glEnableVertexAttribArray = function _glEnableVertexAttribArray(index) { glEnableVertexAttribArray(index); GLEmulation.enabledVertexAttribArrays[index] = 1; if (GLEmulation.currentVao) GLEmulation.currentVao.enabledVertexAttribArrays[index] = 1; }; var glDisableVertexAttribArray = _glDisableVertexAttribArray; - _glDisableVertexAttribArray = function _glDisableVertexAttribArray(index) { + _glDisableVertexAttribArray = _emscripten_glDisableVertexAttribArray = function _glDisableVertexAttribArray(index) { glDisableVertexAttribArray(index); delete GLEmulation.enabledVertexAttribArrays[index]; if (GLEmulation.currentVao) delete GLEmulation.currentVao.enabledVertexAttribArrays[index]; }; var glVertexAttribPointer = _glVertexAttribPointer; - _glVertexAttribPointer = function _glVertexAttribPointer(index, size, type, normalized, stride, pointer) { + _glVertexAttribPointer = _emscripten_glVertexAttribPointer = function _glVertexAttribPointer(index, size, type, normalized, stride, pointer) { glVertexAttribPointer(index, size, type, normalized, stride, pointer); if (GLEmulation.currentVao) { // TODO: avoid object creation here? likely not hot though GLEmulation.currentVao.vertexAttribPointers[index] = [index, size, type, normalized, stride, pointer]; @@ -2311,6 +2316,7 @@ var LibraryGL = { glGetShaderPrecisionFormat__sig: 'v', glGetShaderPrecisionFormat: function() { throw 'glGetShaderPrecisionFormat: TODO' }, + glDeleteObject__deps: ['glDeleteProgram', 'glDeleteShader'], glDeleteObject__sig: 'vi', glDeleteObject: function(id) { if (GL.programs[id]) { @@ -2321,8 +2327,10 @@ var LibraryGL = { Module.printErr('WARNING: deleteObject received invalid id: ' + id); } }, + glDeleteObjectARB: 'glDeleteObject', glGetObjectParameteriv__sig: 'viii', + glGetObjectParameteriv__deps: ['glGetProgramiv', 'glGetShaderiv'], glGetObjectParameteriv: function(id, type, result) { if (GL.programs[id]) { if (type == 0x8B84) { // GL_OBJECT_INFO_LOG_LENGTH_ARB @@ -2343,7 +2351,9 @@ var LibraryGL = { Module.printErr('WARNING: getObjectParameteriv received invalid id: ' + id); } }, + glGetObjectParameterivARB: 'glGetObjectParameteriv', + glGetInfoLog__deps: ['glGetProgramInfoLog', 'glGetShaderInfoLog'], glGetInfoLog__sig: 'viiii', glGetInfoLog: function(id, maxLength, length, infoLog) { if (GL.programs[id]) { @@ -2354,6 +2364,7 @@ var LibraryGL = { Module.printErr('WARNING: getObjectParameteriv received invalid id: ' + id); } }, + glGetInfoLogARB: 'glGetInfoLog', glBindProgram__sig: 'vii', glBindProgram: function(type, id) { @@ -2361,6 +2372,7 @@ var LibraryGL = { assert(id == 0); #endif }, + glBindProgramARB: 'glBindProgram', glGetPointerv: function(name, p) { var attribute; @@ -4108,7 +4120,7 @@ var LibraryGL = { // Replace some functions with immediate-mode aware versions. If there are no client // attributes enabled, and we use webgl-friendly modes (no GL_QUADS), then no need // for emulation - _glDrawArrays = function _glDrawArrays(mode, first, count) { + _glDrawArrays = _emscripten_glDrawArrays = function _glDrawArrays(mode, first, count) { if (GLImmediate.totalEnabledClientAttributes == 0 && mode <= 6) { GLctx.drawArrays(mode, first, count); return; @@ -4124,7 +4136,7 @@ var LibraryGL = { GLImmediate.mode = -1; }; - _glDrawElements = function _glDrawElements(mode, count, type, indices, start, end) { // start, end are given if we come from glDrawRangeElements + _glDrawElements = _emscripten_glDrawElements = function _glDrawElements(mode, count, type, indices, start, end) { // start, end are given if we come from glDrawRangeElements if (GLImmediate.totalEnabledClientAttributes == 0 && mode <= 6 && GL.currElementArrayBuffer) { GLctx.drawElements(mode, count, type, indices); return; @@ -4164,36 +4176,36 @@ var LibraryGL = { } var glActiveTexture = _glActiveTexture; - _glActiveTexture = function _glActiveTexture(texture) { + _glActiveTexture = _emscripten_glActiveTexture = function _glActiveTexture(texture) { GLImmediate.TexEnvJIT.hook_activeTexture(texture); glActiveTexture(texture); }; var glEnable = _glEnable; - _glEnable = function _glEnable(cap) { + _glEnable = _emscripten_glEnable = function _glEnable(cap) { GLImmediate.TexEnvJIT.hook_enable(cap); glEnable(cap); }; var glDisable = _glDisable; - _glDisable = function _glDisable(cap) { + _glDisable = _emscripten_glDisable = function _glDisable(cap) { GLImmediate.TexEnvJIT.hook_disable(cap); glDisable(cap); }; var glTexEnvf = (typeof(_glTexEnvf) != 'undefined') ? _glTexEnvf : function(){}; - _glTexEnvf = function _glTexEnvf(target, pname, param) { + _glTexEnvf = _emscripten_glTexEnvf = function _glTexEnvf(target, pname, param) { GLImmediate.TexEnvJIT.hook_texEnvf(target, pname, param); // Don't call old func, since we are the implementor. //glTexEnvf(target, pname, param); }; var glTexEnvi = (typeof(_glTexEnvi) != 'undefined') ? _glTexEnvi : function(){}; - _glTexEnvi = function _glTexEnvi(target, pname, param) { + _glTexEnvi = _emscripten_glTexEnvi = function _glTexEnvi(target, pname, param) { GLImmediate.TexEnvJIT.hook_texEnvi(target, pname, param); // Don't call old func, since we are the implementor. //glTexEnvi(target, pname, param); }; var glTexEnvfv = (typeof(_glTexEnvfv) != 'undefined') ? _glTexEnvfv : function(){}; - _glTexEnvfv = function _glTexEnvfv(target, pname, param) { + _glTexEnvfv = _emscripten_glTexEnvfv = function _glTexEnvfv(target, pname, param) { GLImmediate.TexEnvJIT.hook_texEnvfv(target, pname, param); // Don't call old func, since we are the implementor. //glTexEnvfv(target, pname, param); @@ -4208,7 +4220,7 @@ var LibraryGL = { }; var glGetIntegerv = _glGetIntegerv; - _glGetIntegerv = function _glGetIntegerv(pname, params) { + _glGetIntegerv = _emscripten_glGetIntegerv = function _glGetIntegerv(pname, params) { switch (pname) { case 0x8B8D: { // GL_CURRENT_PROGRAM // Just query directly so we're working with WebGL objects. @@ -4707,6 +4719,7 @@ var LibraryGL = { // Additional non-GLES rendering calls + glDrawRangeElements__deps: ['glDrawElements'], glDrawRangeElements__sig: 'viiiiii', glDrawRangeElements: function(mode, start, end, count, type, indices) { _glDrawElements(mode, count, type, indices, start, end); @@ -4820,6 +4833,7 @@ var LibraryGL = { if (GLEmulation.currentVao && GLEmulation.currentVao.id == id) GLEmulation.currentVao = null; } }, + glBindVertexArray__deps: ['glBindBuffer', 'glEnableVertexAttribArray', 'glVertexAttribPointer', 'glEnableClientState'], glBindVertexArray__sig: 'vi', glBindVertexArray: function(vao) { // undo vao-related things, wipe the slate clean, both for vao of 0 or an actual vao @@ -5027,39 +5041,11 @@ 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.'; }, + glVertexPointer: function(){ throw 'Legacy GL function (glVertexPointer) called. If you want legacy GL emulation, you need to compile with -s LEGACY_GL_EMULATION=1 to enable legacy GL emulation.'; }, + glGenVertexArrays: function(){ throw 'Legacy GL function (glGenVertexArrays) called. If you want legacy GL emulation, you need to compile with -s LEGACY_GL_EMULATION=1 to enable legacy GL emulation.'; }, + glMatrixMode: function(){ throw 'Legacy GL function (glMatrixMode) called. If you want legacy GL emulation, you need to compile with -s LEGACY_GL_EMULATION=1 to enable legacy GL emulation.'; }, + glBegin: function(){ throw 'Legacy GL function (glBegin) called. If you want legacy GL emulation, you need to compile with -s LEGACY_GL_EMULATION=1 to enable legacy GL emulation.'; }, + glLoadIdentity: function(){ throw 'Legacy GL function (glLoadIdentity) called. If you want legacy GL emulation, you need to compile with -s LEGACY_GL_EMULATION=1 to enable legacy GL emulation.'; }, #endif // LEGACY_GL_EMULATION @@ -5370,53 +5356,37 @@ if (LEGACY_GL_EMULATION) { 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).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[original] = Functions.getIndex(longX, sig); - if (!(longX in Functions.implementedFunctions)) Functions.unimplementedFunctions[longX] = sig; - } - 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'; - tableImpl += '}\nreturn 0;'; - LibraryManager.library.emscripten_procAddressTable = new Function('name', tableImpl); -}, 'emscripten_procAddressTable']; -LibraryGL.emscripten_GetProcAddress = function _LibraryGL_emscripten_GetProcAddress(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; +function copyLibEntry(a, b) { + LibraryGL[a] = LibraryGL[b]; + LibraryGL[a + '__postset'] = LibraryGL[b + '__postset']; + LibraryGL[a + '__sig'] = LibraryGL[b + '__sig']; + LibraryGL[a + '__asm'] = LibraryGL[b + '__asm']; + LibraryGL[a + '__deps'] = LibraryGL[b + '__deps'].slice(0); } +// GL proc address retrieval - allow access through glX and emscripten_glX, to allow name collisions with user-implemented things having the same name (see gl.c) +keys(LibraryGL).forEach(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; + while (typeof LibraryGL[x] === 'string') { + // resolve aliases right here, simpler for fastcomp + copyLibEntry(x, LibraryGL[x]); + } + var y = 'emscripten_' + x; + LibraryGL[x + '__deps'] = LibraryGL[x + '__deps'].map(function(dep) { + // prefix dependencies as well + if (typeof dep === 'string' && dep[0] == 'g' && dep[1] == 'l' && LibraryGL[dep]) { + var orig = dep; + dep = 'emscripten_' + dep; + var fixed = LibraryGL[x].toString().replace(new RegExp('_' + orig + '\\(', 'g'), '_' + dep + '('); + fixed = fixed.substr(0, 9) + '_' + y + fixed.substr(9); + LibraryGL[x] = eval('(function() { return ' + fixed + ' })()'); + } + return dep; + }); + // copy it + copyLibEntry(y, x); +}); + // Final merge mergeInto(LibraryManager.library, LibraryGL); diff --git a/src/library_glfw.js b/src/library_glfw.js index 17e8956a..b54205ad 100644 --- a/src/library_glfw.js +++ b/src/library_glfw.js @@ -197,13 +197,7 @@ var LibraryGLFW = { }, onMouseWheel: function(event) { - if (event.detail > 0) { - GLFW.wheelPos++; - } - - if (event.detail < 0) { - GLFW.wheelPos--; - } + GLFW.wheelPos += Browser.getMouseWheelDelta(event); if (GLFW.mouseWheelFunc && event.target == Module["canvas"]) { Runtime.dynCall('vi', GLFW.mouseWheelFunc, [GLFW.wheelPos]); diff --git a/src/library_glut.js b/src/library_glut.js index 65ac10c4..167e5272 100644 --- a/src/library_glut.js +++ b/src/library_glut.js @@ -229,7 +229,7 @@ var LibraryGLUT = { // cross-browser wheel delta var e = window.event || event; // old IE support - var delta = Math.max(-1, Math.min(1, (e.wheelDelta || -e.detail))); + var delta = -Browser.getMouseWheelDelta(event); var button = 3; // wheel up if (delta < 0) { diff --git a/src/library_html5.js b/src/library_html5.js index 703f9a74..a38c2390 100644 --- a/src/library_html5.js +++ b/src/library_html5.js @@ -709,7 +709,9 @@ var LibraryJSEvents = { var eventHandler = { target: JSEvents.findEventTarget(target), - allowsDeferredCalls: true, + allowsDeferredCalls: false, // XXX Currently disabled, see bug https://bugzilla.mozilla.org/show_bug.cgi?id=966493 + // Once the above bug is resolved, enable the following condition if possible: + // allowsDeferredCalls: eventTypeString == 'touchstart', eventTypeString: eventTypeString, callbackfunc: callbackfunc, handlerFunc: handlerFunc, @@ -980,18 +982,23 @@ var LibraryJSEvents = { if (allowedOrientations & 2) orientations.push("portrait-secondary"); if (allowedOrientations & 4) orientations.push("landscape-primary"); if (allowedOrientations & 8) orientations.push("landscape-secondary"); + var succeeded; if (window.screen.lockOrientation) { - window.screen.lockOrientation(orientations); + succeeded = window.screen.lockOrientation(orientations); } else if (window.screen.mozLockOrientation) { - window.screen.mozLockOrientation(orientations); + succeeded = window.screen.mozLockOrientation(orientations); } else if (window.screen.webkitLockOrientation) { - window.screen.webkitLockOrientation(orientations); + succeeded = window.screen.webkitLockOrientation(orientations); } else if (window.screen.msLockOrientation) { - window.screen.msLockOrientation(orientations); + succeeded = window.screen.msLockOrientation(orientations); } else { return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}}; } - return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + if (succeeded) { + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + } else { + return {{{ cDefine('EMSCRIPTEN_RESULT_FAILED') }}}; + } }, emscripten_unlock_orientation: function() { diff --git a/src/library_idbfs.js b/src/library_idbfs.js index 7f50f17e..91015e77 100644 --- a/src/library_idbfs.js +++ b/src/library_idbfs.js @@ -5,14 +5,12 @@ mergeInto(LibraryManager.library, { indexedDB: function() { return window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB; }, - DB_VERSION: 20, + DB_VERSION: 21, DB_STORE_NAME: 'FILE_DATA', - // reuse all of the core MEMFS functionality mount: function(mount) { + // reuse all of the core MEMFS functionality return MEMFS.mount.apply(null, arguments); }, - // the only custom function IDBFS implements is to handle - // synchronizing the wrapped MEMFS with a backing IDB instance syncfs: function(mount, populate, callback) { IDBFS.getLocalSet(mount, function(err, local) { if (err) return callback(err); @@ -27,103 +25,46 @@ mergeInto(LibraryManager.library, { }); }); }, - reconcile: function(src, dst, callback) { - var total = 0; - - var create = {}; - for (var key in src.files) { - if (!src.files.hasOwnProperty(key)) continue; - var e = src.files[key]; - var e2 = dst.files[key]; - if (!e2 || e.timestamp > e2.timestamp) { - create[key] = e; - total++; - } - } - - var remove = {}; - for (var key in dst.files) { - if (!dst.files.hasOwnProperty(key)) continue; - var e = dst.files[key]; - var e2 = src.files[key]; - if (!e2) { - remove[key] = e; - total++; - } + getDB: function(name, callback) { + // check the cache first + var db = IDBFS.dbs[name]; + if (db) { + return callback(null, db); } - if (!total) { - // early out - return callback(null); + var req; + try { + req = IDBFS.indexedDB().open(name, IDBFS.DB_VERSION); + } catch (e) { + return callback(e); } + req.onupgradeneeded = function(e) { + var db = e.target.result; + var transaction = e.target.transaction; - var completed = 0; - function done(err) { - if (err) return callback(err); - if (++completed >= total) { - return callback(null); - } - }; - - // create a single transaction to handle and IDB reads / writes we'll need to do - var db = src.type === 'remote' ? src.db : dst.db; - var transaction = db.transaction([IDBFS.DB_STORE_NAME], 'readwrite'); - transaction.onerror = function transaction_onerror() { callback(this.error); }; - var store = transaction.objectStore(IDBFS.DB_STORE_NAME); - - for (var path in create) { - if (!create.hasOwnProperty(path)) continue; - var entry = create[path]; + var fileStore; - if (dst.type === 'local') { - // save file to local - try { - if (FS.isDir(entry.mode)) { - FS.mkdir(path, entry.mode); - } else if (FS.isFile(entry.mode)) { - var stream = FS.open(path, 'w+', 0666); - FS.write(stream, entry.contents, 0, entry.contents.length, 0, true /* canOwn */); - FS.close(stream); - } - done(null); - } catch (e) { - return done(e); - } + if (db.objectStoreNames.contains(IDBFS.DB_STORE_NAME)) { + fileStore = transaction.objectStore(IDBFS.DB_STORE_NAME); } else { - // save file to IDB - var req = store.put(entry, path); - req.onsuccess = function req_onsuccess() { done(null); }; - req.onerror = function req_onerror() { done(this.error); }; + fileStore = db.createObjectStore(IDBFS.DB_STORE_NAME); } - } - for (var path in remove) { - if (!remove.hasOwnProperty(path)) continue; - var entry = remove[path]; + fileStore.createIndex('timestamp', 'timestamp', { unique: false }); + }; + req.onsuccess = function() { + db = req.result; - if (dst.type === 'local') { - // delete file from local - try { - if (FS.isDir(entry.mode)) { - // TODO recursive delete? - FS.rmdir(path); - } else if (FS.isFile(entry.mode)) { - FS.unlink(path); - } - done(null); - } catch (e) { - return done(e); - } - } else { - // delete file from IDB - var req = store.delete(path); - req.onsuccess = function req_onsuccess() { done(null); }; - req.onerror = function req_onerror() { done(this.error); }; - } - } + // add to the cache + IDBFS.dbs[name] = db; + callback(null, db); + }; + req.onerror = function() { + callback(this.error); + }; }, getLocalSet: function(mount, callback) { - var files = {}; + var entries = {}; function isRealDir(p) { return p !== '.' && p !== '..'; @@ -134,83 +75,192 @@ mergeInto(LibraryManager.library, { } }; - var check = FS.readdir(mount.mountpoint) - .filter(isRealDir) - .map(toAbsolute(mount.mountpoint)); + var check = FS.readdir(mount.mountpoint).filter(isRealDir).map(toAbsolute(mount.mountpoint)); while (check.length) { var path = check.pop(); - var stat, node; + var stat; try { - var lookup = FS.lookupPath(path); - node = lookup.node; stat = FS.stat(path); } catch (e) { return callback(e); } if (FS.isDir(stat.mode)) { - check.push.apply(check, FS.readdir(path) - .filter(isRealDir) - .map(toAbsolute(path))); - - files[path] = { mode: stat.mode, timestamp: stat.mtime }; - } else if (FS.isFile(stat.mode)) { - files[path] = { contents: node.contents, mode: stat.mode, timestamp: stat.mtime }; - } else { - return callback(new Error('node type not supported')); + check.push.apply(check, FS.readdir(path).filter(isRealDir).map(toAbsolute(path))); } - } - return callback(null, { type: 'local', files: files }); - }, - getDB: function(name, callback) { - // look it up in the cache - var db = IDBFS.dbs[name]; - if (db) { - return callback(null, db); - } - var req; - try { - req = IDBFS.indexedDB().open(name, IDBFS.DB_VERSION); - } catch (e) { - return onerror(e); + entries[path] = { timestamp: stat.mtime }; } - req.onupgradeneeded = function req_onupgradeneeded() { - db = req.result; - db.createObjectStore(IDBFS.DB_STORE_NAME); - }; - req.onsuccess = function req_onsuccess() { - db = req.result; - // add to the cache - IDBFS.dbs[name] = db; - callback(null, db); - }; - req.onerror = function req_onerror() { - callback(this.error); - }; + + return callback(null, { type: 'local', entries: entries }); }, getRemoteSet: function(mount, callback) { - var files = {}; + var entries = {}; IDBFS.getDB(mount.mountpoint, function(err, db) { if (err) return callback(err); var transaction = db.transaction([IDBFS.DB_STORE_NAME], 'readonly'); - transaction.onerror = function transaction_onerror() { callback(this.error); }; + transaction.onerror = function() { callback(this.error); }; var store = transaction.objectStore(IDBFS.DB_STORE_NAME); - store.openCursor().onsuccess = function store_openCursor_onsuccess(event) { + var index = store.index('timestamp'); + + index.openKeyCursor().onsuccess = function(event) { var cursor = event.target.result; + if (!cursor) { - return callback(null, { type: 'remote', db: db, files: files }); + return callback(null, { type: 'remote', db: db, entries: entries }); } - files[cursor.key] = cursor.value; + entries[cursor.primaryKey] = { timestamp: cursor.key }; + cursor.continue(); }; }); + }, + loadLocalEntry: function(path, callback) { + var stat, node; + + try { + var lookup = FS.lookupPath(path); + node = lookup.node; + stat = FS.stat(path); + } catch (e) { + return callback(e); + } + + if (FS.isDir(stat.mode)) { + return callback(null, { timestamp: stat.mtime, mode: stat.mode }); + } else if (FS.isFile(stat.mode)) { + return callback(null, { timestamp: stat.mtime, mode: stat.mode, contents: node.contents }); + } else { + return callback(new Error('node type not supported')); + } + }, + storeLocalEntry: function(path, entry, callback) { + try { + if (FS.isDir(entry.mode)) { + FS.mkdir(path, entry.mode); + } else if (FS.isFile(entry.mode)) { + FS.writeFile(path, entry.contents, { encoding: 'binary', canOwn: true }); + } else { + return callback(new Error('node type not supported')); + } + + FS.utime(path, entry.timestamp, entry.timestamp); + } catch (e) { + return callback(e); + } + + callback(null); + }, + removeLocalEntry: function(path, callback) { + try { + var lookup = FS.lookupPath(path); + var stat = FS.stat(path); + + if (FS.isDir(stat.mode)) { + FS.rmdir(path); + } else if (FS.isFile(stat.mode)) { + FS.unlink(path); + } + } catch (e) { + return callback(e); + } + + callback(null); + }, + loadRemoteEntry: function(store, path, callback) { + var req = store.get(path); + req.onsuccess = function(event) { callback(null, event.target.result); }; + req.onerror = function() { callback(this.error); }; + }, + storeRemoteEntry: function(store, path, entry, callback) { + var req = store.put(entry, path); + req.onsuccess = function() { callback(null); }; + req.onerror = function() { callback(this.error); }; + }, + removeRemoteEntry: function(store, path, callback) { + var req = store.delete(path); + req.onsuccess = function() { callback(null); }; + req.onerror = function() { callback(this.error); }; + }, + reconcile: function(src, dst, callback) { + var total = 0; + + var create = []; + Object.keys(src.entries).forEach(function (key) { + var e = src.entries[key]; + var e2 = dst.entries[key]; + if (!e2 || e.timestamp > e2.timestamp) { + create.push(key); + total++; + } + }); + + var remove = []; + Object.keys(dst.entries).forEach(function (key) { + var e = dst.entries[key]; + var e2 = src.entries[key]; + if (!e2) { + remove.push(key); + total++; + } + }); + + if (!total) { + return callback(null); + } + + var errored = false; + var completed = 0; + var db = src.type === 'remote' ? src.db : dst.db; + var transaction = db.transaction([IDBFS.DB_STORE_NAME], 'readwrite'); + var store = transaction.objectStore(IDBFS.DB_STORE_NAME); + + function done(err) { + if (err) { + if (!done.errored) { + done.errored = true; + return callback(err); + } + return; + } + if (++completed >= total) { + return callback(null); + } + }; + + transaction.onerror = function() { done(this.error); }; + + // sort paths in ascending order so directory entries are created + // before the files inside them + create.sort().forEach(function (path) { + if (dst.type === 'local') { + IDBFS.loadRemoteEntry(store, path, function (err, entry) { + if (err) return done(err); + IDBFS.storeLocalEntry(path, entry, done); + }); + } else { + IDBFS.loadLocalEntry(path, function (err, entry) { + if (err) return done(err); + IDBFS.storeRemoteEntry(store, path, entry, done); + }); + } + }); + + // sort paths in descending order so files are deleted before their + // parent directories + remove.sort().reverse().forEach(function(path) { + if (dst.type === 'local') { + IDBFS.removeLocalEntry(path, done); + } else { + IDBFS.removeRemoteEntry(store, path, done); + } + }); } } }); diff --git a/src/library_openal.js b/src/library_openal.js index 67481824..ac49fe95 100644 --- a/src/library_openal.js +++ b/src/library_openal.js @@ -5,6 +5,10 @@ var LibraryOpenAL = { $AL: { contexts: [], currentContext: null, + + stringCache: {}, + alcStringCache: {}, + QUEUE_INTERVAL: 25, QUEUE_LOOKAHEAD: 100, @@ -235,6 +239,42 @@ var LibraryOpenAL = { return _alGetError(); }, + alcGetIntegerv: function(device, param, size, data) { + if (size == 0 || !data) { + AL.currentContext.err = 0xA003 /* AL_INVALID_VALUE */; + return; + } + + switch(param) { + case 0x1000 /* ALC_MAJOR_VERSION */: + {{{ makeSetValue('data', '0', '1', 'i32') }}}; + break; + case 0x1001 /* ALC_MINOR_VERSION */: + {{{ makeSetValue('data', '0', '1', 'i32') }}}; + break; + case 0x1002 /* ALC_ATTRIBUTES_SIZE */: + if (!device) { + AL.currentContext.err = 0xA001 /* ALC_INVALID_DEVICE */; + return 0; + } + {{{ makeSetValue('data', '0', '1', 'i32') }}}; + break; + case 0x1003 /* ALC_ALL_ATTRIBUTES */: + if (!device) { + AL.currentContext.err = 0xA001 /* ALC_INVALID_DEVICE */; + return 0; + } + {{{ makeSetValue('data', '0', '0', 'i32') }}}; + break; + default: +#if OPENAL_DEBUG + console.log("alcGetIntegerv with param " + param + " not implemented yet"); +#endif + AL.currentContext.err = 0xA003 /* ALC_INVALID_ENUM */; + break; + } + }, + alDeleteSources: function(count, sources) { if (!AL.currentContext) { #if OPENAL_DEBUG @@ -334,6 +374,18 @@ var LibraryOpenAL = { } }, + alIsSource: function(sourceId) { + if (!AL.currentContext) { + return false; + } + + if (!AL.currentContext.src[sourceId - 1]) { + return false; + } else { + return true; + } + }, + alSourcei__deps: ['updateSource'], alSourcei: function(source, param, value) { if (!AL.currentContext) { @@ -691,6 +743,7 @@ var LibraryOpenAL = { } try { AL.currentContext.buf[buffer - 1] = AL.currentContext.ctx.createBuffer(channels, size / (bytes * channels), freq); + AL.currentContext.buf[buffer - 1].bytesPerSample = bytes; } catch (e) { AL.currentContext.err = 0xA003 /* AL_INVALID_VALUE */; return; @@ -715,6 +768,41 @@ var LibraryOpenAL = { } }, + alGetBufferi: function(buffer, param, value) + { + if (!AL.currentContext) { +#if OPENAL_DEBUG + console.error("alGetBufferi called without a valid context"); +#endif + return; + } + var buf = AL.currentContext.buf[buffer - 1]; + if (!buf) { +#if OPENAL_DEBUG + console.error("alGetBufferi called with an invalid buffer"); +#endif + AL.currentContext.err = 0xA001 /* AL_INVALID_NAME */; + return; + } + switch (param) { + case 0x2001 /* AL_FREQUENCY */: + {{{ makeSetValue('value', '0', 'buf.sampleRate', 'i32') }}}; + break; + case 0x2002 /* AL_BITS */: + {{{ makeSetValue('value', '0', 'buf.bytesPerSample * 8', 'i32') }}}; + break; + case 0x2003 /* AL_CHANNELS */: + {{{ makeSetValue('value', '0', 'buf.numberOfChannels', 'i32') }}}; + break; + case 0x2004 /* AL_SIZE */: + {{{ makeSetValue('value', '0', 'buf.length * buf.bytesPerSample * buf.numberOfChannels', 'i32') }}}; + break; + default: + AL.currentContext.err = 0xA002 /* AL_INVALID_ENUM */; + break; + } + }, + alSourcePlay__deps: ['setSourceState'], alSourcePlay: function(source) { if (!AL.currentContext) { @@ -1128,15 +1216,116 @@ var LibraryOpenAL = { }, alGetString: function(param) { - return allocate(intArrayFromString('NA'), 'i8', ALLOC_NORMAL); + if (AL.stringCache[param]) return AL.stringCache[param]; + var ret; + switch (param) { + case 0 /* AL_NO_ERROR */: + ret = 'No Error'; + break; + case 0xA001 /* AL_INVALID_NAME */: + ret = 'Invalid Name'; + break; + case 0xA002 /* AL_INVALID_ENUM */: + ret = 'Invalid Enum'; + break; + case 0xA003 /* AL_INVALID_VALUE */: + ret = 'Invalid Value'; + break; + case 0xA004 /* AL_INVALID_OPERATION */: + ret = 'Invalid Operation'; + break; + case 0xA005 /* AL_OUT_OF_MEMORY */: + ret = 'Out of Memory'; + break; + case 0xB001 /* AL_VENDOR */: + ret = 'Emscripten'; + break; + case 0xB002 /* AL_VERSION */: + ret = '1.1'; + break; + case 0xB003 /* AL_RENDERER */: + ret = 'WebAudio'; + break; + case 0xB004 /* AL_EXTENSIONS */: + ret = ''; + break; + default: + AL.currentContext.err = 0xA002 /* AL_INVALID_ENUM */; + return 0; + } + + ret = allocate(intArrayFromString(ret), 'i8', ALLOC_NORMAL); + + AL.stringCache[param] = ret; + + return ret; }, alGetProcAddress: function(fname) { return 0; }, - alcGetString: function(param) { - return allocate(intArrayFromString('NA'), 'i8', ALLOC_NORMAL); + alcGetString: function(device, param) { + if (AL.alcStringCache[param]) return AL.alcStringCache[param]; + var ret; + switch (param) { + case 0 /* ALC_NO_ERROR */: + ret = 'No Error'; + break; + case 0xA001 /* ALC_INVALID_DEVICE */: + ret = 'Invalid Device'; + break; + case 0xA002 /* ALC_INVALID_CONTEXT */: + ret = 'Invalid Context'; + break; + case 0xA003 /* ALC_INVALID_ENUM */: + ret = 'Invalid Enum'; + break; + case 0xA004 /* ALC_INVALID_VALUE */: + ret = 'Invalid Value'; + break; + case 0xA005 /* ALC_OUT_OF_MEMORY */: + ret = 'Out of Memory'; + break; + case 0x1004 /* ALC_DEFAULT_DEVICE_SPECIFIER */: + if (typeof(AudioContext) == "function" || + typeof(webkitAudioContext) == "function") { + ret = 'Device'; + } else { + return 0; + } + break; + case 0x1005 /* ALC_DEVICE_SPECIFIER */: + if (typeof(AudioContext) == "function" || + typeof(webkitAudioContext) == "function") { + ret = 'Device\0'; + } else { + ret = '\0'; + } + break; + case 0x311 /* ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER */: + return 0; + break; + case 0x310 /* ALC_CAPTURE_DEVICE_SPECIFIER */: + ret = '\0' + break; + case 0x1006 /* ALC_EXTENSIONS */: + if (!device) { + AL.currentContext.err = 0xA001 /* ALC_INVALID_DEVICE */; + return 0; + } + ret = ''; + break; + default: + AL.currentContext.err = 0xA003 /* ALC_INVALID_ENUM */; + return 0; + } + + ret = allocate(intArrayFromString(ret), 'i8', ALLOC_NORMAL); + + AL.alcStringCache[param] = ret; + + return ret; }, alcGetProcAddress: function(device, fname) { diff --git a/src/library_sdl.js b/src/library_sdl.js index 80734d95..cadc3aee 100644 --- a/src/library_sdl.js +++ b/src/library_sdl.js @@ -214,27 +214,33 @@ var LibrarySDL = { makeSurface: function(width, height, flags, usePageCanvas, source, rmask, gmask, bmask, amask) { flags = flags || 0; - var surf = _malloc({{{ C_STRUCTS.SDL_Surface.__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({{{ C_STRUCTS.SDL_PixelFormat.__size__ }}}); - flags |= 1; // SDL_HWSURFACE - this tells SDL_MUSTLOCK that this needs to be locked + var is_SDL_HWSURFACE = flags & 0x00000001; + var is_SDL_HWPALETTE = flags & 0x00200000; + var is_SDL_OPENGL = flags & 0x04000000; + var surf = _malloc({{{ C_STRUCTS.SDL_Surface.__size__ }}}); + var pixelFormat = _malloc({{{ C_STRUCTS.SDL_PixelFormat.__size__ }}}); //surface with SDL_HWPALETTE flag is 8bpp surface (1 byte) - var is_SDL_HWPALETTE = flags & 0x00200000; var bpp = is_SDL_HWPALETTE ? 1 : 4; - - {{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.flags, 'flags', 'i32') }}}; // SDL_Surface.flags - {{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.format, 'pixelFormat', 'void*') }}};// SDL_Surface.format TODO - {{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.w, 'width', 'i32') }}}; // SDL_Surface.w - {{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.h, 'height', 'i32') }}}; // SDL_Surface.h - {{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.pitch, 'width * bpp', 'i32') }}}; // SDL_Surface.pitch, assuming RGBA or indexed for now, - // since that is what ImageData gives us in browsers - {{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.pixels, 'buffer', 'void*') }}}; // SDL_Surface.pixels - {{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.clip_rect, '0', 'i32*') }}}; // SDL_Surface.offset + var buffer = 0; + + // preemptively initialize this for software surfaces, + // otherwise it will be lazily initialized inside of SDL_LockSurface + if (!is_SDL_HWSURFACE && !is_SDL_OPENGL) { + buffer = _malloc(width * height * 4); + } + {{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.flags, 'flags', 'i32') }}}; + {{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.format, 'pixelFormat', 'void*') }}}; + {{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.w, 'width', 'i32') }}}; + {{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.h, 'height', 'i32') }}}; + {{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.pitch, 'width * bpp', 'i32') }}}; // assuming RGBA or indexed for now, + // since that is what ImageData gives us in browsers + {{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.pixels, 'buffer', 'void*') }}}; + {{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.clip_rect, '0', 'i32*') }}}; {{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.refcount, '1', 'i32') }}}; - {{{ makeSetValue('pixelFormat', C_STRUCTS.SDL_PixelFormat.format, cDefine('SDL_PIXELFORMAT_RGBA8888'), 'i32') }}};// SDL_PIXELFORMAT_RGBA8888 + {{{ makeSetValue('pixelFormat', C_STRUCTS.SDL_PixelFormat.format, cDefine('SDL_PIXELFORMAT_RGBA8888'), 'i32') }}}; {{{ makeSetValue('pixelFormat', C_STRUCTS.SDL_PixelFormat.palette, '0', 'i32') }}};// TODO {{{ makeSetValue('pixelFormat', C_STRUCTS.SDL_PixelFormat.BitsPerPixel, 'bpp * 8', 'i8') }}}; {{{ makeSetValue('pixelFormat', C_STRUCTS.SDL_PixelFormat.BytesPerPixel, 'bpp', 'i8') }}}; @@ -245,8 +251,7 @@ var LibrarySDL = { {{{ makeSetValue('pixelFormat', C_STRUCTS.SDL_PixelFormat.Amask, 'amask || 0xff000000', 'i32') }}}; // Decide if we want to use WebGL or not - var useWebGL = (flags & 0x04000000) != 0; // SDL_OPENGL - SDL.GL = SDL.GL || useWebGL; + SDL.GL = SDL.GL || is_SDL_OPENGL; var canvas; if (!usePageCanvas) { if (SDL.canvasPool.length > 0) { @@ -266,7 +271,7 @@ var LibrarySDL = { stencil: (SDL.glAttributes[7 /*SDL_GL_STENCIL_SIZE*/] > 0) }; - var ctx = Browser.createContext(canvas, useWebGL, usePageCanvas, webGLContextAttributes); + var ctx = Browser.createContext(canvas, is_SDL_OPENGL, usePageCanvas, webGLContextAttributes); SDL.surfaces[surf] = { width: width, @@ -337,7 +342,7 @@ var LibrarySDL = { var info = SDL.surfaces[surf]; if (!info.usePageCanvas && info.canvas) SDL.canvasPool.push(info.canvas); - _free(info.buffer); + if (info.buffer) _free(info.buffer); _free(info.pixelFormat); _free(surf); SDL.surfaces[surf] = null; @@ -407,12 +412,12 @@ var LibrarySDL = { // won't fire. However, it's fine (and in some cases necessary) to // preventDefault for keys that don't generate a character. Otherwise, // preventDefault is the right thing to do in general. - if (event.type !== 'keydown' || (event.keyCode === 8 /* backspace */ || event.keyCode === 9 /* tab */)) { + if (event.type !== 'keydown' || (!SDL.unicode && !SDL.textInput) || (event.keyCode === 8 /* backspace */ || event.keyCode === 9 /* tab */)) { event.preventDefault(); } if (event.type == 'DOMMouseScroll' || event.type == 'mousewheel') { - var button = (event.type == 'DOMMouseScroll' ? event.detail : -event.wheelDelta) > 0 ? 4 : 3; + var button = Browser.getMouseWheelDelta(event) > 0 ? 4 : 3; var event2 = { type: 'mousedown', button: button, @@ -701,6 +706,29 @@ var LibrarySDL = { return ret; }, + fillWebAudioBufferFromHeap: function(heapPtr, sizeSamplesPerChannel, dstAudioBuffer) { + // The input audio data is interleaved across the channels, i.e. [L, R, L, R, L, R, ...] and is either 8-bit or 16-bit as + // supported by the SDL API. The output audio wave data for Web Audio API must be in planar buffers of [-1,1]-normalized Float32 data, + // so perform a buffer conversion for the data. + var numChannels = SDL.audio.channels; + for(var c = 0; c < numChannels; ++c) { + var channelData = dstAudioBuffer['getChannelData'](c); + if (channelData.length != sizeSamplesPerChannel) { + throw 'Web Audio output buffer length mismatch! Destination size: ' + channelData.length + ' samples vs expected ' + sizeSamplesPerChannel + ' samples!'; + } + if (SDL.audio.format == 0x8010 /*AUDIO_S16LSB*/) { + for(var j = 0; j < sizeSamplesPerChannel; ++j) { + channelData[j] = ({{{ makeGetValue('heapPtr', '(j*numChannels + c)*2', 'i16', 0, 0) }}}) / 0x8000; + } + } else if (SDL.audio.format == 0x0008 /*AUDIO_U8*/) { + for(var j = 0; j < sizeSamplesPerChannel; ++j) { + var v = ({{{ makeGetValue('heapPtr', 'j*numChannels + c', 'i8', 0, 0) }}}); + channelData[j] = ((v >= 0) ? v-128 : v+128) /128; + } + } + } + }, + // Debugging debugSurface: function(surfData) { @@ -986,6 +1014,11 @@ var LibrarySDL = { surfData.locked++; if (surfData.locked > 1) return 0; + if (!surfData.buffer) { + surfData.buffer = _malloc(surfData.width * surfData.height * 4); + {{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.pixels, 'surfData.buffer', 'void*') }}}; + } + // Mark in C/C++-accessible SDL structure // SDL_Surface has the following fields: Uint32 flags, SDL_PixelFormat *format; int w, h; Uint16 pitch; void *pixels; ... // So we have fields all of the same size, and 5 of them before us. @@ -1045,8 +1078,9 @@ var LibrarySDL = { var surfData = SDL.surfaces[surf]; - surfData.locked--; - if (surfData.locked > 0) return; + if (!surfData.locked || --surfData.locked > 0) { + return; + } // Copy pixel data to image if (surfData.isFlagSet(0x00200000 /* SDL_HWPALETTE */)) { @@ -1220,6 +1254,11 @@ var LibrarySDL = { return SDL.errorMessage; }, + SDL_SetError: function() {}, + + SDL_Malloc: 'malloc', + SDL_Free: 'free', + SDL_CreateRGBSurface: function(flags, width, height, depth, rmask, gmask, bmask, amask) { return SDL.makeSurface(width, height, flags, false, 'CreateRGBSurface', rmask, gmask, bmask, amask); }, @@ -1723,6 +1762,7 @@ var LibrarySDL = { SDL.audio.pushAudio=function(ptr,sizeBytes) { try { --SDL.audio.numAudioTimersPending; + if (SDL.audio.paused) return; var sizeSamples = sizeBytes / SDL.audio.bytesPerSample; // How many samples fit in the callback buffer? var sizeSamplesPerChannel = sizeSamples / SDL.audio.channels; // How many samples per a single channel fit in the cb buffer? @@ -1738,26 +1778,7 @@ var LibrarySDL = { var soundBuffer = SDL.audioContext['createBuffer'](SDL.audio.channels,sizeSamplesPerChannel,SDL.audio.freq); SDL.audio.soundSource[SDL.audio.nextSoundSource]['connect'](SDL.audioContext['destination']); - // The input audio data is interleaved across the channels, i.e. [L, R, L, R, L, R, ...] and is either 8-bit or 16-bit as - // supported by the SDL API. The output audio wave data for Web Audio API must be in planar buffers of [-1,1]-normalized Float32 data, - // so perform a buffer conversion for the data. - var numChannels = SDL.audio.channels; - for(var i = 0; i < numChannels; ++i) { - var channelData = soundBuffer['getChannelData'](i); - if (channelData.length != sizeSamplesPerChannel) { - throw 'Web Audio output buffer length mismatch! Destination size: ' + channelData.length + ' samples vs expected ' + sizeSamplesPerChannel + ' samples!'; - } - if (SDL.audio.format == 0x8010 /*AUDIO_S16LSB*/) { - for(var j = 0; j < sizeSamplesPerChannel; ++j) { - channelData[j] = ({{{ makeGetValue('ptr', '(j*numChannels + i)*2', 'i16', 0, 0) }}}) / 0x8000; - } - } else if (SDL.audio.format == 0x0008 /*AUDIO_U8*/) { - for(var j = 0; j < sizeSamplesPerChannel; ++j) { - var v = ({{{ makeGetValue('ptr', 'j*numChannels + i', 'i8', 0, 0) }}}); - channelData[j] = ((v >= 0) ? v-128 : v+128) /128; - } - } - } + SDL.fillWebAudioBufferFromHeap(ptr, sizeSamplesPerChannel, soundBuffer); // Workaround https://bugzilla.mozilla.org/show_bug.cgi?id=883675 by setting the buffer only after filling. The order is important here! source['buffer'] = soundBuffer; @@ -1773,18 +1794,18 @@ var LibrarySDL = { SDL.audio.soundSource[SDL.audio.nextSoundSource]['start'](playtime); var buffer_duration = sizeSamplesPerChannel / SDL.audio.freq; SDL.audio.nextPlayTime = playtime + buffer_duration; - SDL.audio.nextSoundSource = (SDL.audio.nextSoundSource + 1) % 4; + // Timer will be scheduled before the buffer completed playing. + // Extra buffers are needed to avoid disturbing playing buffer. + SDL.audio.nextSoundSource = (SDL.audio.nextSoundSource + 1) % (SDL.audio.numSimultaneouslyQueuedBuffers + 2); var secsUntilNextCall = playtime-curtime; // Queue the next audio frame push to be performed when the previously queued buffer has finished playing. - if (SDL.audio.numAudioTimersPending == 0) { - var preemptBufferFeedMSecs = buffer_duration/2.0; - SDL.audio.timer = Browser.safeSetTimeout(SDL.audio.caller, Math.max(0.0, 1000.0*secsUntilNextCall-preemptBufferFeedMSecs)); - ++SDL.audio.numAudioTimersPending; - } + var preemptBufferFeedMSecs = 1000*buffer_duration/2.0; + SDL.audio.timer = Browser.safeSetTimeout(SDL.audio.caller, Math.max(0.0, 1000.0*secsUntilNextCall-preemptBufferFeedMSecs)); + ++SDL.audio.numAudioTimersPending; // If we are risking starving, immediately queue extra buffers. - if (secsUntilNextCall <= buffer_duration && SDL.audio.numAudioTimersPending < SDL.audio.numSimultaneouslyQueuedBuffers) { + if (SDL.audio.numAudioTimersPending < SDL.audio.numSimultaneouslyQueuedBuffers) { ++SDL.audio.numAudioTimersPending; Browser.safeSetTimeout(SDL.audio.caller, 1.0); } @@ -1836,11 +1857,27 @@ var LibrarySDL = { SDL.audio.numAudioTimersPending = 0; SDL.audio.timer = undefined; } - } else if (!SDL.audio.timer) { - // Start the audio playback timer callback loop. - SDL.audio.numAudioTimersPending = 1; - SDL.audio.timer = Browser.safeSetTimeout(SDL.audio.caller, 1); - SDL.audio.startTime = Date.now() / 1000.0; // Only used for Mozilla Audio Data API. Not needed for Web Audio API. + if (SDL.audio.scriptProcessorNode !== undefined) { + SDL.audio.scriptProcessorNode['disconnect'](); + SDL.audio.scriptProcessorNode = undefined; + } + } else if (!SDL.audio.timer && !SDL.audio.scriptProcessorNode) { + // If we are using the same sampling frequency as the native sampling rate of the Web Audio graph is using, we can feed our buffers via + // Web Audio ScriptProcessorNode, which is a pull-mode API that calls back to our code to get audio data. + if (SDL.audioContext !== undefined && SDL.audio.freq == SDL.audioContext['sampleRate']) { + var sizeSamplesPerChannel = SDL.audio.bufferSize / SDL.audio.bytesPerSample / SDL.audio.channels; // How many samples per a single channel fit in the cb buffer? + SDL.audio.scriptProcessorNode = SDL.audioContext['createScriptProcessor'](sizeSamplesPerChannel, 0, SDL.audio.channels); + SDL.audio.scriptProcessorNode['onaudioprocess'] = function (e) { + Runtime.dynCall('viii', SDL.audio.callback, [SDL.audio.userdata, SDL.audio.buffer, SDL.audio.bufferSize]); + SDL.fillWebAudioBufferFromHeap(SDL.audio.buffer, sizeSamplesPerChannel, e['outputBuffer']); + } + SDL.audio.scriptProcessorNode['connect'](SDL.audioContext['destination']); + } else { // If we are using a different sampling rate, must manually queue audio data to the graph via timers. + // Start the audio playback timer callback loop. + SDL.audio.numAudioTimersPending = 1; + SDL.audio.timer = Browser.safeSetTimeout(SDL.audio.caller, 1); + SDL.audio.startTime = Date.now() / 1000.0; // Only used for Mozilla Audio Data API. Not needed for Web Audio API. + } } SDL.audio.paused = pauseOn; }, @@ -2482,7 +2519,7 @@ var LibrarySDL = { SDL_GL_GetProcAddress__deps: ['emscripten_GetProcAddress'], SDL_GL_GetProcAddress: function(name_) { - return _emscripten_GetProcAddress(Pointer_stringify(name_)); + return _emscripten_GetProcAddress(name_); }, SDL_GL_SwapBuffers: function() {}, @@ -2676,6 +2713,14 @@ var LibrarySDL = { } }, + SDL_GetNumAudioDrivers: function() { return 1 }, + SDL_GetCurrentAudioDriver: function() { + return allocate(intArrayFromString('Emscripten Audio'), 'i8', ALLOC_NORMAL); + }, + + SDL_GetAudioDriver__deps: ['SDL_GetCurrentAudioDriver'], + SDL_GetAudioDriver: function(index) { return _SDL_GetCurrentAudioDriver() }, + SDL_EnableUNICODE: function(on) { var ret = SDL.unicode || 0; SDL.unicode = on; @@ -2705,6 +2750,9 @@ var LibrarySDL = { SDL_WM_IconifyWindow: function() { throw 'SDL_WM_IconifyWindow TODO' }, Mix_SetPostMix: function() { Runtime.warnOnce('Mix_SetPostMix: TODO') }, + + Mix_VolumeChunk: function(chunk, volume) { throw 'Mix_VolumeChunk: TODO' }, + Mix_SetPosition: function(channel, angle, distance) { throw 'Mix_SetPosition: TODO' }, Mix_QuerySpec: function() { throw 'Mix_QuerySpec: TODO' }, Mix_FadeInChannelTimed: function() { throw 'Mix_FadeInChannelTimed' }, Mix_FadeOutChannel: function() { throw 'Mix_FadeOutChannel' }, diff --git a/src/parseTools.js b/src/parseTools.js index bad080b7..fe56580e 100644 --- a/src/parseTools.js +++ b/src/parseTools.js @@ -162,7 +162,7 @@ function isArrayType(type) { function isStructType(type) { if (isPointerType(type)) return false; if (isArrayType(type)) return true; - if (/<?{ ?[^}]* ?}>?/.test(type)) return true; // { i32, i8 } etc. - anonymous struct types + if (/<?\{ ?[^}]* ?\}>?/.test(type)) return true; // { i32, i8 } etc. - anonymous struct types // See comment in isStructPointerType() return type[0] == '%'; } @@ -172,7 +172,7 @@ function isVectorType(type) { } function isStructuralType(type) { - return /^{ ?[^}]* ?}$/.test(type); // { i32, i8 } etc. - anonymous struct types + return /^\{ ?[^}]* ?\}$/.test(type); // { i32, i8 } etc. - anonymous struct types } function getStructuralTypeParts(type) { // split { i32, i8 } etc. into parts diff --git a/src/postamble.js b/src/postamble.js index 90a86474..382d3117 100644 --- a/src/postamble.js +++ b/src/postamble.js @@ -202,6 +202,10 @@ if (Module['noInitialRun']) { shouldRunNow = false; } +#if NO_EXIT_RUNTIME +Module["noExitRuntime"] = true; +#endif + run(); // {{POST_RUN_ADDITIONS}} diff --git a/src/preamble.js b/src/preamble.js index 9a65b092..5038e9c4 100644 --- a/src/preamble.js +++ b/src/preamble.js @@ -188,6 +188,8 @@ function SAFE_HEAP_STORE(dest, value, bytes, isFloat) { #endif assert(dest > 0, 'segmentation fault'); assert(dest % bytes === 0); + assert(dest < Math.max(DYNAMICTOP, STATICTOP)); + assert(DYNAMICTOP <= TOTAL_MEMORY); setValue(dest, value, getSafeHeapType(bytes, isFloat), 1); } @@ -197,6 +199,8 @@ function SAFE_HEAP_LOAD(dest, bytes, isFloat, unsigned) { #endif assert(dest > 0, 'segmentation fault'); assert(dest % bytes === 0); + assert(dest < Math.max(DYNAMICTOP, STATICTOP)); + assert(DYNAMICTOP <= TOTAL_MEMORY); var type = getSafeHeapType(bytes, isFloat); var ret = getValue(dest, type, 1); if (unsigned) ret = unSign(ret, parseInt(type.substr(1)), 1); @@ -1023,6 +1027,11 @@ function preMain() { } function exitRuntime() { +#if ASSERTIONS + if (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) { + Module.printErr('Exiting runtime. Any attempt to access the compiled C code may fail from now. If you want to keep the runtime alive, set Module["noExitRuntime"] = true or build with -s NO_EXIT_RUNTIME=1'); + } +#endif callRuntimeCallbacks(__ATEXIT__); } diff --git a/src/runtime.js b/src/runtime.js index 4fcca56b..a9265e70 100644 --- a/src/runtime.js +++ b/src/runtime.js @@ -565,7 +565,7 @@ function getRuntime() { // Converts a value we have as signed, into an unsigned value. For // example, -1 in int32 would be a very large number as unsigned. -function unSign(value, bits, ignore, sig) { +function unSign(value, bits, ignore) { if (value >= 0) { return value; } @@ -578,7 +578,7 @@ function unSign(value, bits, ignore, sig) { // Converts a value we have as unsigned, into a signed value. For // example, 200 in a uint8 would be a negative number. -function reSign(value, bits, ignore, sig) { +function reSign(value, bits, ignore) { if (value <= 0) { return value; } diff --git a/src/settings.js b/src/settings.js index 720fb53f..1db91dca 100644 --- a/src/settings.js +++ b/src/settings.js @@ -48,6 +48,8 @@ var VERBOSE = 0; // When set to 1, will generate more verbose output during comp var INVOKE_RUN = 1; // Whether we will run the main() function. Disable if you embed the generated // code in your own, and will call main() yourself at the right time (which you // can do with Module.callMain(), with an optional parameter of commandline args). +var NO_EXIT_RUNTIME = 0; // If set, the runtime is not quit when main() completes (allowing code to + // run afterwards, for example from the browser main event loop). var INIT_HEAP = 0; // Whether to initialize memory anywhere other than the stack to 0. var TOTAL_STACK = 5*1024*1024; // The total stack size. There is no way to enlarge the stack, so this // value must be large enough for the program's requirements. If @@ -104,6 +106,9 @@ var FORCE_ALIGNED_MEMORY = 0; // If enabled, assumes all reads and writes are fu // for ways to help find places in your code where unaligned reads/writes are done - // you might be able to refactor your codebase to prevent them, which leads to // smaller and faster code, or even the option to turn this flag on. +var WARN_UNALIGNED = 0; // Warn at compile time about instructions that LLVM tells us are not fully aligned. + // This is useful to find places in your code where you might refactor to ensure proper + // alignment. (this option is fastcomp-only) var PRECISE_I64_MATH = 1; // If enabled, i64 addition etc. is emulated - which is slow but precise. If disabled, // we use the 'double trick' which is fast but incurs rounding at high values. // Note that we do not catch 32-bit multiplication by default (which must be done in @@ -121,8 +126,11 @@ var PRECISE_F32 = 0; // 0: Use JS numbers for floating-point values. These are 6 // 1: Model C++ floats precisely, using Math.fround, polyfilling when necessary. This // can be slow if the polyfill is used on heavy float32 computation. // 2: Model C++ floats precisely using Math.fround if available in the JS engine, otherwise - // use an empty polyfill. This will have less of a speed penalty than using the full - // polyfill in cases where engine support is not present. + // use an empty polyfill. This will have much less of a speed penalty than using the full + // polyfill in cases where engine support is not present. In addition, we can + // remove the empty polyfill calls themselves on the client when generating html, + // which should mean that this gives you the best of both worlds of 0 and 1, and is + // therefore recommended. var SIMD = 0; // Whether to emit SIMD code ( https://github.com/johnmccutchan/ecmascript_simd ) var CLOSURE_ANNOTATIONS = 0; // If set, the generated code will be annotated for the closure @@ -251,8 +259,8 @@ var DISABLE_EXCEPTION_CATCHING = 0; // Disables generating code to actually catc // TODO: Make this also remove cxa_begin_catch etc., optimize relooper // for it, etc. (perhaps do all of this as preprocessing on .ll?) -var EXCEPTION_CATCHING_WHITELIST = []; // Enables catching exception in listed functions if - // DISABLE_EXCEPTION_CATCHING = 2 set +var EXCEPTION_CATCHING_WHITELIST = []; // Enables catching exception in the listed functions only, if + // DISABLE_EXCEPTION_CATCHING = 2 is set var EXECUTION_TIMEOUT = -1; // Throw an exception after X seconds - useful to debug infinite loops var CHECK_OVERFLOWS = 0; // Add code that checks for overflows in integer math operations. diff --git a/src/struct_info.json b/src/struct_info.json index 32261c0a..2aeffc9c 100644 --- a/src/struct_info.json +++ b/src/struct_info.json @@ -141,7 +141,9 @@ }, { "file": "libc/stdlib.h", - "defines": [], + "defines": [ + "RAND_MAX" + ], "structs": { // NOTE: The hash sign at the end of this name is a hint to the processor that it mustn't prefix "struct " to the name to reference this struct. // It will be stripped away when writing the compiled JSON file. You can just refer to it as C_STRUCTS.div_t when using it in the JS code. |