diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/embind/embind.js | 130 | ||||
-rw-r--r-- | src/library.js | 38 | ||||
-rw-r--r-- | src/library_browser.js | 71 | ||||
-rw-r--r-- | src/library_fs.js | 63 | ||||
-rw-r--r-- | src/library_glfw.js | 2 | ||||
-rw-r--r-- | src/library_sdl.js | 1 | ||||
-rw-r--r-- | src/parseTools.js | 11 | ||||
-rw-r--r-- | src/preamble.js | 20 | ||||
-rw-r--r-- | src/relooper/Relooper.cpp | 11 | ||||
-rw-r--r-- | src/settings.js | 11 | ||||
-rw-r--r-- | src/shell.html | 7 | ||||
-rw-r--r-- | src/shell_minimal.html | 7 |
12 files changed, 270 insertions, 102 deletions
diff --git a/src/embind/embind.js b/src/embind/embind.js index 6ec07cd9..4821c77b 100644 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -1,4 +1,4 @@ -/*global Module*/ +/*global Module, asm*/ /*global _malloc, _free, _memcpy*/ /*global FUNCTION_TABLE, HEAP8, HEAPU8, HEAP16, HEAPU16, HEAP32, HEAPU32, HEAPF32, HEAPF64*/ /*global readLatin1String*/ @@ -622,10 +622,6 @@ function craftInvokerFunction(humanName, argTypes, classType, cppInvokerFunc, cp var isClassMethodFunc = (argTypes[1] !== null && classType !== null); - if (!isClassMethodFunc && !FUNCTION_TABLE[cppTargetFunc]) { - throwBindingError('Global function '+humanName+' is not defined!'); - } - // Free functions with signature "void function()" do not need an invoker that marshalls between wire types. // TODO: This omits argument count check - enable only at -O3 or similar. // if (ENABLE_UNSAFE_OPTS && argCount == 2 && argTypes[0].name == "void" && !isClassMethodFunc) { @@ -662,8 +658,8 @@ function craftInvokerFunction(humanName, argTypes, classType, cppInvokerFunc, cp } var dtorStack = needsDestructorStack ? "destructors" : "null"; - var args1 = ["throwBindingError", "classType", "invoker", "fn", "runDestructors", "retType", "classParam"]; - var args2 = [throwBindingError, classType, cppInvokerFunc, cppTargetFunc, runDestructors, argTypes[0], argTypes[1]]; + var args1 = ["throwBindingError", "invoker", "fn", "runDestructors", "retType", "classParam"]; + var args2 = [throwBindingError, cppInvokerFunc, cppTargetFunc, runDestructors, argTypes[0], argTypes[1]]; if (isClassMethodFunc) { invokerFnBody += "var thisWired = classParam.toWireType("+dtorStack+", this);\n"; @@ -708,10 +704,50 @@ function craftInvokerFunction(humanName, argTypes, classType, cppInvokerFunc, cp return invokerFunction; } -function __embind_register_function(name, argCount, rawArgTypesAddr, rawInvoker, fn) { +function requireFunction(signature, rawFunction) { + signature = readLatin1String(signature); + var fp; + // asm.js does not define FUNCTION_TABLE + if (typeof FUNCTION_TABLE === "undefined") { + // asm.js does not give direct access to the function tables, + // and thus we must go through the dynCall interface which allows + // calling into a signature's function table by pointer value. + // + // https://github.com/dherman/asm.js/issues/83 + // + // This has three main penalties: + // - dynCall is another function call in the path from JavaScript to C++. + // - JITs may not predict through the function table indirection at runtime. + // - Function.prototype.bind generally benchmarks poorly relative to + // function objects, but using 'arguments' would confound JITs and + // possibly allocate. + var dc = asm['dynCall_' + signature]; + if (dc === undefined) { + // We will always enter this branch if the signature + // contains 'f' and PRECISE_F32 is not enabled. + // + // Try again, replacing 'f' with 'd'. + dc = asm['dynCall_' + signature.replace(/f/g, 'd')]; + if (dc === undefined) { + throwBindingError("No dynCall invoker for signature: " + signature); + } + } + fp = dc.bind(undefined, rawFunction); + } else { + fp = FUNCTION_TABLE[rawFunction]; + } + + if (typeof fp !== "function") { + throwBindingError("unknown function pointer with signature " + signature + ": " + rawFunction); + } + return fp; +} + +function __embind_register_function(name, argCount, rawArgTypesAddr, signature, rawInvoker, fn) { var argTypes = heap32VectorToArray(argCount, rawArgTypesAddr); name = readLatin1String(name); - rawInvoker = FUNCTION_TABLE[rawInvoker]; + + rawInvoker = requireFunction(signature, rawInvoker); exposePublicSymbol(name, function() { throwUnboundTypeError('Cannot call ' + name + ' due to unbound types', argTypes); @@ -726,11 +762,11 @@ function __embind_register_function(name, argCount, rawArgTypesAddr, rawInvoker, var tupleRegistrations = {}; -function __embind_register_value_array(rawType, name, rawConstructor, rawDestructor) { +function __embind_register_value_array(rawType, name, constructorSignature, rawConstructor, destructorSignature, rawDestructor) { tupleRegistrations[rawType] = { name: readLatin1String(name), - rawConstructor: FUNCTION_TABLE[rawConstructor], - rawDestructor: FUNCTION_TABLE[rawDestructor], + rawConstructor: requireFunction(constructorSignature, rawConstructor), + rawDestructor: requireFunction(destructorSignature, rawDestructor), elements: [], }; } @@ -738,18 +774,20 @@ function __embind_register_value_array(rawType, name, rawConstructor, rawDestruc function __embind_register_value_array_element( rawTupleType, getterReturnType, + getterSignature, getter, getterContext, setterArgumentType, + setterSignature, setter, setterContext ) { tupleRegistrations[rawTupleType].elements.push({ getterReturnType: getterReturnType, - getter: FUNCTION_TABLE[getter], + getter: requireFunction(getterSignature, getter), getterContext: getterContext, setterArgumentType: setterArgumentType, - setter: FUNCTION_TABLE[setter], + setter: requireFunction(setterSignature, setter), setterContext: setterContext, }); } @@ -818,13 +856,15 @@ var structRegistrations = {}; function __embind_register_value_object( rawType, name, + constructorSignature, rawConstructor, + destructorSignature, rawDestructor ) { structRegistrations[rawType] = { name: readLatin1String(name), - rawConstructor: FUNCTION_TABLE[rawConstructor], - rawDestructor: FUNCTION_TABLE[rawDestructor], + rawConstructor: requireFunction(constructorSignature, rawConstructor), + rawDestructor: requireFunction(destructorSignature, rawDestructor), fields: [], }; } @@ -833,19 +873,21 @@ function __embind_register_value_object_field( structType, fieldName, getterReturnType, + getterSignature, getter, getterContext, setterArgumentType, + setterSignature, setter, setterContext ) { structRegistrations[structType].fields.push({ fieldName: readLatin1String(fieldName), getterReturnType: getterReturnType, - getter: FUNCTION_TABLE[getter], + getter: requireFunction(getterSignature, getter), getterContext: getterContext, setterArgumentType: setterArgumentType, - setter: FUNCTION_TABLE[setter], + setter: requireFunction(setterSignature, setter), setterContext: setterContext, }); } @@ -1324,17 +1366,25 @@ function __embind_register_class( rawPointerType, rawConstPointerType, baseClassRawType, + getActualTypeSignature, getActualType, + upcastSignature, upcast, + downcastSignature, downcast, name, + destructorSignature, rawDestructor ) { name = readLatin1String(name); - rawDestructor = FUNCTION_TABLE[rawDestructor]; - getActualType = FUNCTION_TABLE[getActualType]; - upcast = FUNCTION_TABLE[upcast]; - downcast = FUNCTION_TABLE[downcast]; + getActualType = requireFunction(getActualTypeSignature, getActualType); + if (upcast) { + upcast = requireFunction(upcastSignature, upcast); + } + if (downcast) { + downcast = requireFunction(downcastSignature, downcast); + } + rawDestructor = requireFunction(destructorSignature, rawDestructor); var legalFunctionName = makeLegalFunctionName(name); exposePublicSymbol(legalFunctionName, function() { @@ -1424,11 +1474,12 @@ function __embind_register_class_constructor( rawClassType, argCount, rawArgTypesAddr, + invokerSignature, invoker, rawConstructor ) { var rawArgTypes = heap32VectorToArray(argCount, rawArgTypesAddr); - invoker = FUNCTION_TABLE[invoker]; + invoker = requireFunction(invokerSignature, invoker); whenDependentTypesAreResolved([], [rawClassType], function(classType) { classType = classType[0]; @@ -1474,9 +1525,12 @@ function downcastPointer(ptr, ptrClass, desiredClass) { if (undefined === desiredClass.baseClass) { return null; // no conversion } - // O(depth) stack space used - return desiredClass.downcast( - downcastPointer(ptr, ptrClass, desiredClass.baseClass)); + + var rv = downcastPointer(ptr, ptrClass, desiredClass.baseClass); + if (rv === null) { + return null; + } + return desiredClass.downcast(rv); } function upcastPointer(ptr, ptrClass, desiredClass) { @@ -1513,12 +1567,13 @@ function __embind_register_class_function( methodName, argCount, rawArgTypesAddr, // [ReturnType, ThisType, Args...] + invokerSignature, rawInvoker, context ) { var rawArgTypes = heap32VectorToArray(argCount, rawArgTypesAddr); methodName = readLatin1String(methodName); - rawInvoker = FUNCTION_TABLE[rawInvoker]; + rawInvoker = requireFunction(invokerSignature, rawInvoker); whenDependentTypesAreResolved([], [rawClassType], function(classType) { classType = classType[0]; @@ -1564,12 +1619,13 @@ function __embind_register_class_class_function( methodName, argCount, rawArgTypesAddr, + invokerSignature, rawInvoker, fn ) { var rawArgTypes = heap32VectorToArray(argCount, rawArgTypesAddr); methodName = readLatin1String(methodName); - rawInvoker = FUNCTION_TABLE[rawInvoker]; + rawInvoker = requireFunction(invokerSignature, rawInvoker); whenDependentTypesAreResolved([], [rawClassType], function(classType) { classType = classType[0]; var humanName = classType.name + '.' + methodName; @@ -1609,14 +1665,16 @@ function __embind_register_class_property( classType, fieldName, getterReturnType, + getterSignature, getter, getterContext, setterArgumentType, + setterSignature, setter, setterContext ) { fieldName = readLatin1String(fieldName); - getter = FUNCTION_TABLE[getter]; + getter = requireFunction(getterSignature, getter); whenDependentTypesAreResolved([], [classType], function(classType) { classType = classType[0]; @@ -1654,7 +1712,7 @@ function __embind_register_class_property( }; if (setter) { - setter = FUNCTION_TABLE[setter]; + setter = requireFunction(setterSignature, setter); var setterArgumentType = types[1]; desc.set = function(v) { var ptr = validateThis(this, classType, humanName + ' setter'); @@ -1689,16 +1747,20 @@ function __embind_register_smart_ptr( rawPointeeType, name, sharingPolicy, + getPointeeSignature, rawGetPointee, + constructorSignature, rawConstructor, + shareSignature, rawShare, + destructorSignature, rawDestructor ) { name = readLatin1String(name); - rawGetPointee = FUNCTION_TABLE[rawGetPointee]; - rawConstructor = FUNCTION_TABLE[rawConstructor]; - rawShare = FUNCTION_TABLE[rawShare]; - rawDestructor = FUNCTION_TABLE[rawDestructor]; + rawGetPointee = requireFunction(getPointeeSignature, rawGetPointee); + rawConstructor = requireFunction(constructorSignature, rawConstructor); + rawShare = requireFunction(shareSignature, rawShare); + rawDestructor = requireFunction(destructorSignature, rawDestructor); whenDependentTypesAreResolved([rawType], [rawPointeeType], function(pointeeType) { pointeeType = pointeeType[0]; diff --git a/src/library.js b/src/library.js index c2830397..0d649258 100644 --- a/src/library.js +++ b/src/library.js @@ -762,12 +762,18 @@ LibraryManager.library = { // http://pubs.opengroup.org/onlinepubs/000095399/functions/crypt.html // TODO: Implement (probably compile from C). ___setErrNo(ERRNO_CODES.ENOSYS); +#if ASSERTIONS + Runtime.warnOnce('crypt() returning an error as we do not support it'); +#endif return 0; }, encrypt: function(block, edflag) { // void encrypt(char block[64], int edflag); // http://pubs.opengroup.org/onlinepubs/000095399/functions/encrypt.html // TODO: Implement (probably compile from C). +#if ASSERTIONS + Runtime.warnOnce('encrypt() returning an error as we do not support it'); +#endif ___setErrNo(ERRNO_CODES.ENOSYS); }, fpathconf__deps: ['__setErrNo', '$ERRNO_CODES'], @@ -940,6 +946,9 @@ LibraryManager.library = { // It is possible to implement this using two device streams, but pipes make // little sense in a single-threaded environment, so we do not support them. ___setErrNo(ERRNO_CODES.ENOSYS); +#if ASSERTIONS + Runtime.warnOnce('pipe() returning an error as we do not support them'); +#endif return -1; }, pread__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], @@ -1584,7 +1593,6 @@ LibraryManager.library = { return /^[+-]?[0-9]*\.?[0-9]+([eE][+-]?[0-9]+)?/.exec(text); }, - // TODO: Document. _scanString__deps: ['_getFloat'], _scanString: function(format, get, unget, varargs) { if (!__scanString.whiteSpace) { @@ -1726,6 +1734,7 @@ LibraryManager.library = { } var long_ = false; var half = false; + var quarter = false; var longLong = false; if (format[formatIndex] == 'l') { long_ = true; @@ -1737,6 +1746,10 @@ LibraryManager.library = { } else if (format[formatIndex] == 'h') { half = true; formatIndex++; + if (format[formatIndex] == 'h') { + quarter = true; + formatIndex++; + } } var type = format[formatIndex]; formatIndex++; @@ -1795,20 +1808,21 @@ LibraryManager.library = { var text = buffer.join(''); var argPtr = {{{ makeGetValue('varargs', 'argIndex', 'void*') }}}; argIndex += Runtime.getAlignSize('void*', null, true); + var base = 10; switch (type) { + case 'X': case 'x': + base = 16; case 'd': case 'u': case 'i': - if (half) { - {{{ makeSetValue('argPtr', 0, 'parseInt(text, 10)', 'i16') }}}; + if (quarter) { + {{{ makeSetValue('argPtr', 0, 'parseInt(text, base)', 'i8') }}}; + } else if (half) { + {{{ makeSetValue('argPtr', 0, 'parseInt(text, base)', 'i16') }}}; } else if (longLong) { - {{{ makeSetValue('argPtr', 0, 'parseInt(text, 10)', 'i64') }}}; + {{{ makeSetValue('argPtr', 0, 'parseInt(text, base)', 'i64') }}}; } else { - {{{ makeSetValue('argPtr', 0, 'parseInt(text, 10)', 'i32') }}}; + {{{ makeSetValue('argPtr', 0, 'parseInt(text, base)', 'i32') }}}; } break; - case 'X': - case 'x': - {{{ makeSetValue('argPtr', 0, 'parseInt(text, 16)', 'i32') }}}; - break; case 'F': case 'f': case 'E': @@ -5777,7 +5791,7 @@ LibraryManager.library = { pattern = pattern.replace(new RegExp('\\%'+pattern[i+1], 'g'), ''); } - var matches = new RegExp('^'+pattern).exec(Pointer_stringify(buf)) + var matches = new RegExp('^'+pattern, "i").exec(Pointer_stringify(buf)) // Module['print'](Pointer_stringify(buf)+ ' is matched by '+((new RegExp('^'+pattern)).source)+' into: '+JSON.stringify(matches)); function initDate() { @@ -6203,8 +6217,10 @@ LibraryManager.library = { raise__deps: ['$ERRNO_CODES', '__setErrNo'], raise: function(sig) { - // TODO: ___setErrNo(ERRNO_CODES.ENOSYS); +#if ASSERTIONS + Runtime.warnOnce('raise() returning an error as we do not support it'); +#endif return -1; }, diff --git a/src/library_browser.js b/src/library_browser.js index 4be7315e..44e8c473 100644 --- a/src/library_browser.js +++ b/src/library_browser.js @@ -196,41 +196,42 @@ mergeInto(LibraryManager.library, { // Canvas event setup var canvas = Module['canvas']; - - // forced aspect ratio can be enabled by defining 'forcedAspectRatio' on Module - // Module['forcedAspectRatio'] = 4 / 3; - - canvas.requestPointerLock = canvas['requestPointerLock'] || - canvas['mozRequestPointerLock'] || - canvas['webkitRequestPointerLock'] || - canvas['msRequestPointerLock'] || - function(){}; - canvas.exitPointerLock = document['exitPointerLock'] || - document['mozExitPointerLock'] || - document['webkitExitPointerLock'] || - document['msExitPointerLock'] || - function(){}; // no-op if function does not exist - canvas.exitPointerLock = canvas.exitPointerLock.bind(document); - - function pointerLockChange() { - Browser.pointerLock = document['pointerLockElement'] === canvas || - document['mozPointerLockElement'] === canvas || - document['webkitPointerLockElement'] === canvas || - document['msPointerLockElement'] === canvas; - } + if (canvas) { + // forced aspect ratio can be enabled by defining 'forcedAspectRatio' on Module + // Module['forcedAspectRatio'] = 4 / 3; + + canvas.requestPointerLock = canvas['requestPointerLock'] || + canvas['mozRequestPointerLock'] || + canvas['webkitRequestPointerLock'] || + canvas['msRequestPointerLock'] || + function(){}; + canvas.exitPointerLock = document['exitPointerLock'] || + document['mozExitPointerLock'] || + document['webkitExitPointerLock'] || + document['msExitPointerLock'] || + function(){}; // no-op if function does not exist + canvas.exitPointerLock = canvas.exitPointerLock.bind(document); + + function pointerLockChange() { + Browser.pointerLock = document['pointerLockElement'] === canvas || + document['mozPointerLockElement'] === canvas || + document['webkitPointerLockElement'] === canvas || + document['msPointerLockElement'] === canvas; + } - document.addEventListener('pointerlockchange', pointerLockChange, false); - document.addEventListener('mozpointerlockchange', pointerLockChange, false); - document.addEventListener('webkitpointerlockchange', pointerLockChange, false); - document.addEventListener('mspointerlockchange', pointerLockChange, false); + document.addEventListener('pointerlockchange', pointerLockChange, false); + document.addEventListener('mozpointerlockchange', pointerLockChange, false); + document.addEventListener('webkitpointerlockchange', pointerLockChange, false); + document.addEventListener('mspointerlockchange', pointerLockChange, false); - if (Module['elementPointerLock']) { - canvas.addEventListener("click", function(ev) { - if (!Browser.pointerLock && canvas.requestPointerLock) { - canvas.requestPointerLock(); - ev.preventDefault(); - } - }, false); + if (Module['elementPointerLock']) { + canvas.addEventListener("click", function(ev) { + if (!Browser.pointerLock && canvas.requestPointerLock) { + canvas.requestPointerLock(); + ev.preventDefault(); + } + }, false); + } } }, @@ -429,11 +430,13 @@ mergeInto(LibraryManager.library, { }); }, safeSetTimeout: function(func, timeout) { + Module['noExitRuntime'] = true; return setTimeout(function() { if (!ABORT) func(); }, timeout); }, safeSetInterval: function(func, timeout) { + Module['noExitRuntime'] = true; return setInterval(function() { if (!ABORT) func(); }, timeout); @@ -886,6 +889,8 @@ mergeInto(LibraryManager.library, { emscripten_set_main_loop: function(func, fps, simulateInfiniteLoop, arg) { Module['noExitRuntime'] = true; + assert(!Browser.mainLoop.scheduler, 'there can only be one main loop function at once: call emscripten_cancel_main_loop to cancel the previous one, if you want to'); + Browser.mainLoop.runner = function Browser_mainLoop_runner() { if (ABORT) return; if (Browser.mainLoop.queue.length > 0) { diff --git a/src/library_fs.js b/src/library_fs.js index d53210f9..5f7f1dea 100644 --- a/src/library_fs.js +++ b/src/library_fs.js @@ -26,7 +26,13 @@ mergeInto(LibraryManager.library, { // This is set to false when the runtime is initialized, allowing you // to modify the filesystem freely before run() is called. ignorePermissions: true, - + trackingDelegate: {}, + tracking: { + openFlags: { + READ: 1 << 0, + WRITE: 1 << 1 + } + }, ErrnoError: null, // set during init genericErrors: {}, @@ -713,6 +719,13 @@ mergeInto(LibraryManager.library, { throw new FS.ErrnoError(err); } } + try { + if (FS.trackingDelegate['willMovePath']) { + FS.trackingDelegate['willMovePath'](old_path, new_path); + } + } catch(e) { + console.log("FS.trackingDelegate['willMovePath']('"+old_path+"', '"+new_path+"') threw an exception: " + e.message); + } // remove the node from the lookup hash FS.hashRemoveNode(old_node); // do the underlying fs rename @@ -725,6 +738,11 @@ mergeInto(LibraryManager.library, { // changed its name) FS.hashAddNode(old_node); } + try { + if (FS.trackingDelegate['onMovePath']) FS.trackingDelegate['onMovePath'](old_path, new_path); + } catch(e) { + console.log("FS.trackingDelegate['onMovePath']('"+old_path+"', '"+new_path+"') threw an exception: " + e.message); + } }, rmdir: function(path) { var lookup = FS.lookupPath(path, { parent: true }); @@ -741,8 +759,20 @@ mergeInto(LibraryManager.library, { if (FS.isMountpoint(node)) { throw new FS.ErrnoError(ERRNO_CODES.EBUSY); } + try { + if (FS.trackingDelegate['willDeletePath']) { + FS.trackingDelegate['willDeletePath'](path); + } + } catch(e) { + console.log("FS.trackingDelegate['willDeletePath']('"+path+"') threw an exception: " + e.message); + } parent.node_ops.rmdir(parent, name); FS.destroyNode(node); + try { + if (FS.trackingDelegate['onDeletePath']) FS.trackingDelegate['onDeletePath'](path); + } catch(e) { + console.log("FS.trackingDelegate['onDeletePath']('"+path+"') threw an exception: " + e.message); + } }, readdir: function(path) { var lookup = FS.lookupPath(path, { follow: true }); @@ -769,8 +799,20 @@ mergeInto(LibraryManager.library, { if (FS.isMountpoint(node)) { throw new FS.ErrnoError(ERRNO_CODES.EBUSY); } + try { + if (FS.trackingDelegate['willDeletePath']) { + FS.trackingDelegate['willDeletePath'](path); + } + } catch(e) { + console.log("FS.trackingDelegate['willDeletePath']('"+path+"') threw an exception: " + e.message); + } parent.node_ops.unlink(parent, name); FS.destroyNode(node); + try { + if (FS.trackingDelegate['onDeletePath']) FS.trackingDelegate['onDeletePath'](path); + } catch(e) { + console.log("FS.trackingDelegate['onDeletePath']('"+path+"') threw an exception: " + e.message); + } }, readlink: function(path) { var lookup = FS.lookupPath(path); @@ -965,6 +1007,20 @@ mergeInto(LibraryManager.library, { Module['printErr']('read file: ' + path); } } + try { + if (FS.trackingDelegate['onOpenFile']) { + var trackingFlags = 0; + if ((flags & {{{ cDefine('O_ACCMODE') }}}) !== {{{ cDefine('O_WRONLY') }}}) { + trackingFlags |= FS.tracking.openFlags.READ; + } + if ((flags & {{{ cDefine('O_ACCMODE') }}}) !== {{{ cDefine('O_RDONLY') }}}) { + trackingFlags |= FS.tracking.openFlags.WRITE; + } + FS.trackingDelegate['onOpenFile'](path, trackingFlags); + } + } catch(e) { + console.log("FS.trackingDelegate['onOpenFile']('"+path+"', flags) threw an exception: " + e.message); + } return stream; }, close: function(stream) { @@ -1034,6 +1090,11 @@ mergeInto(LibraryManager.library, { } var bytesWritten = stream.stream_ops.write(stream, buffer, offset, length, position, canOwn); if (!seeking) stream.position += bytesWritten; + try { + if (stream.path && FS.trackingDelegate['onWriteToFile']) FS.trackingDelegate['onWriteToFile'](stream.path); + } catch(e) { + console.log("FS.trackingDelegate['onWriteToFile']('"+path+"') threw an exception: " + e.message); + } return bytesWritten; }, allocate: function(stream, offset, length) { diff --git a/src/library_glfw.js b/src/library_glfw.js index 0b3fccd4..6d539326 100644 --- a/src/library_glfw.js +++ b/src/library_glfw.js @@ -211,7 +211,7 @@ var LibraryGLFW = { }, onMouseWheel: function(event) { - GLFW.wheelPos += Browser.getMouseWheelDelta(event); + GLFW.wheelPos -= Browser.getMouseWheelDelta(event); if (GLFW.mouseWheelFunc && event.target == Module["canvas"]) { Runtime.dynCall('vi', GLFW.mouseWheelFunc, [GLFW.wheelPos]); diff --git a/src/library_sdl.js b/src/library_sdl.js index eedb8c48..077f72eb 100644 --- a/src/library_sdl.js +++ b/src/library_sdl.js @@ -686,7 +686,6 @@ var LibrarySDL = { } else { code = SDL.keyCodes[event.keyCode] || event.keyCode; } - {{{ makeSetValue('SDL.keyboardState', 'code', 'down', 'i8') }}}; // TODO: lmeta, rmeta, numlock, capslock, KMOD_MODE, KMOD_RESERVED SDL.modState = ({{{ makeGetValue('SDL.keyboardState', '1248', 'i8') }}} ? 0x0040 | 0x0080 : 0) | // KMOD_LCTRL & KMOD_RCTRL diff --git a/src/parseTools.js b/src/parseTools.js index 4396abd9..0c413afa 100644 --- a/src/parseTools.js +++ b/src/parseTools.js @@ -1168,15 +1168,10 @@ function getHeapOffset(offset, type, forceAsm) { var sz = Runtime.getNativeTypeSize(type); var shifts = Math.log(sz)/Math.LN2; offset = '(' + offset + ')'; - if (shifts != 0) { - if (CHECK_HEAP_ALIGN) { - return '((CHECK_ALIGN_' + sz + '(' + offset + '|0)|0)>>' + shifts + ')'; - } else { - return '(' + offset + '>>' + shifts + ')'; - } + if (CHECK_HEAP_ALIGN && shifts > 0) { + return '((CHECK_ALIGN_' + sz + '(' + offset + '|0)|0)>>' + shifts + ')'; } else { - // we need to guard against overflows here, HEAP[U]8 expects a guaranteed int - return isJSVar(offset) ? offset : '(' + offset + '|0)'; + return '(' + offset + '>>' + shifts + ')'; } } diff --git a/src/preamble.js b/src/preamble.js index 89ab5026..2aec94c6 100644 --- a/src/preamble.js +++ b/src/preamble.js @@ -186,17 +186,17 @@ function SAFE_HEAP_STORE(dest, value, bytes, isFloat) { #if SAFE_HEAP_LOG Module.print('SAFE_HEAP store: ' + [dest, value, bytes, isFloat]); #endif - assert(dest > 0, 'segmentation fault'); - assert(dest % bytes === 0, 'alignment error'); - assert(dest < Math.max(DYNAMICTOP, STATICTOP), 'segmentation fault (high)'); + if (dest <= 0) abort('segmentation fault storing ' + bytes + ' bytes to address ' + dest); + if (dest % bytes !== 0) abort('alignment error storing to address ' + dest + ', which was expected to be aligned to a multiple of ' + bytes); + if (dest + bytes > Math.max(DYNAMICTOP, STATICTOP)) abort('segmentation fault, exceeded the top of the available heap when storing ' + bytes + ' bytes to address ' + dest + '. STATICTOP=' + STATICTOP + ', DYNAMICTOP=' + DYNAMICTOP); assert(DYNAMICTOP <= TOTAL_MEMORY); setValue(dest, value, getSafeHeapType(bytes, isFloat), 1); } function SAFE_HEAP_LOAD(dest, bytes, isFloat, unsigned) { - assert(dest > 0, 'segmentation fault'); - assert(dest % bytes === 0, 'alignment error'); - assert(dest < Math.max(DYNAMICTOP, STATICTOP), 'segmentation fault (high)'); + if (dest <= 0) abort('segmentation fault loading ' + bytes + ' bytes from address ' + dest); + if (dest % bytes !== 0) abort('alignment error loading from address ' + dest + ', which was expected to be aligned to a multiple of ' + bytes); + if (dest + bytes > Math.max(DYNAMICTOP, STATICTOP)) abort('segmentation fault, exceeded the top of the available heap when loading ' + bytes + ' bytes from address ' + dest + '. STATICTOP=' + STATICTOP + ', DYNAMICTOP=' + DYNAMICTOP); assert(DYNAMICTOP <= TOTAL_MEMORY); var type = getSafeHeapType(bytes, isFloat); var ret = getValue(dest, type, 1); @@ -207,6 +207,14 @@ function SAFE_HEAP_LOAD(dest, bytes, isFloat, unsigned) { return ret; } +function SAFE_FT_MASK(value, mask) { + var ret = value & mask; + if (ret !== value) { + abort('Function table mask error: function pointer is ' + value + ' which is masked by ' + mask + ', the likely cause of this is that the function pointer is being called by the wrong type.'); + } + return ret; +} + #endif #endif diff --git a/src/relooper/Relooper.cpp b/src/relooper/Relooper.cpp index ce9232d9..568dd381 100644 --- a/src/relooper/Relooper.cpp +++ b/src/relooper/Relooper.cpp @@ -17,6 +17,10 @@ typedef std::string ministring; #endif +// uncomment these out to get LLVM errs() debugging support +//#include <llvm/Support/raw_ostream.h> +//using namespace llvm; + template <class T, class U> static bool contains(const T& container, const U& contained) { return container.count(contained); } @@ -202,6 +206,7 @@ void Block::Render(bool InLoop) { if (Fused) { PrintDebug("Fusing Multiple to Simple\n"); Parent->Next = Parent->Next->Next; + Fused->UseSwitch = false; // TODO: emit switches here Fused->RenderLoopPrefix(); // When the Multiple has the same number of groups as we have branches, @@ -319,11 +324,7 @@ void MultipleShape::RenderLoopPrefix() { } } else { if (Labeled) { - if (UseSwitch) { - PrintIndented("L%d: ", Id); - } else { - PrintIndented("L%d: do {\n", Id); - } + PrintIndented("L%d: do {\n", Id); } else { PrintIndented("do {\n"); } diff --git a/src/settings.js b/src/settings.js index bf16290b..3289eace 100644 --- a/src/settings.js +++ b/src/settings.js @@ -124,13 +124,20 @@ var PRECISE_I32_MUL = 1; // If enabled, i32 multiplication is done with full pre var PRECISE_F32 = 0; // 0: Use JS numbers for floating-point values. These are 64-bit and do not model C++ // floats exactly, which are 32-bit. // 1: Model C++ floats precisely, using Math.fround, polyfilling when necessary. This - // can be slow if the polyfill is used on heavy float32 computation. + // can be slow if the polyfill is used on heavy float32 computation. See note on + // browser support below. // 2: Model C++ floats precisely using Math.fround if available in the JS engine, otherwise // 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. + // XXX Note: To optimize float32-using code, we use the 'const' keyword in the emitted + // code. This allows us to avoid unnecessary calls to Math.fround, which would + // slow down engines not yet supporting that function. 'const' is present in + // all modern browsers, including Firefox, Chrome and Safari, but in IE is only + // present in IE11 and above. Therefore if you need to support legacy versions of + // IE, you should not enable PRECISE_F32 1 or 2. var SIMD = 0; // Whether to emit SIMD code ( https://github.com/johnmccutchan/ecmascript_simd ) var CLOSURE_COMPILER = 0; // Whether closure compiling is being run on this output @@ -338,7 +345,7 @@ var EXPORTED_FUNCTIONS = ['_main', '_malloc']; var EXPORT_ALL = 0; // If true, we export all the symbols. Note that this does *not* affect LLVM, so it can // still eliminate functions as dead. This just exports them on the Module object. var EXPORT_BINDINGS = 0; // Export all bindings generator functions (prefixed with emscripten_bind_). This - // is necessary to use the bindings generator with asm.js + // is necessary to use the WebIDL binder or bindings generator with asm.js var RETAIN_COMPILER_SETTINGS = 0; // Remembers the values of these settings, and makes them accessible // through Runtime.getCompilerSetting and emscripten_get_compiler_setting. // To see what is retained, look for compilerSettings in the generated code. diff --git a/src/shell.html b/src/shell.html index 226f12b9..1e213024 100644 --- a/src/shell.html +++ b/src/shell.html @@ -1273,6 +1273,13 @@ } }; Module.setStatus('Downloading...'); + window.onerror = function() { + Module.setStatus('Exception thrown, see JavaScript console'); + spinnerElement.style.display = 'none'; + Module.setStatus = function(text) { |