aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/embind/embind.js130
-rw-r--r--src/library.js38
-rw-r--r--src/library_browser.js71
-rw-r--r--src/library_fs.js63
-rw-r--r--src/library_glfw.js2
-rw-r--r--src/library_sdl.js1
-rw-r--r--src/parseTools.js11
-rw-r--r--src/preamble.js20
-rw-r--r--src/relooper/Relooper.cpp11
-rw-r--r--src/settings.js11
-rw-r--r--src/shell.html7
-rw-r--r--src/shell_minimal.html7
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) {