aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/intertyper.js3
-rw-r--r--src/jsifier.js31
-rw-r--r--src/library.js41
-rw-r--r--src/library_gl.js80
-rw-r--r--src/library_sdl.js234
-rw-r--r--src/modules.js48
-rw-r--r--src/parseTools.js99
-rw-r--r--src/preamble.js17
-rw-r--r--src/runtime.js4
-rw-r--r--src/settings.js8
-rw-r--r--src/struct_info.json15
-rw-r--r--src/utility.js3
12 files changed, 441 insertions, 142 deletions
diff --git a/src/intertyper.js b/src/intertyper.js
index fceeb38d..fa53c652 100644
--- a/src/intertyper.js
+++ b/src/intertyper.js
@@ -680,6 +680,9 @@ function intertyper(lines, sidePass, baseLineNums) {
args.push(ident);
});
}
+ item.ident = expandLLVMString(item.ident).replace(/(#[^\n]*)/g, function(m) {
+ return '/* ' + m.substr(1) + ' */'; // fix asm comments to js comments
+ });
if (item.assignTo) item.ident = 'return ' + item.ident;
item.ident = '(function(' + params + ') { ' + item.ident + ' })(' + args + ');';
return { ret: item, item: item };
diff --git a/src/jsifier.js b/src/jsifier.js
index 0da48a8c..97317756 100644
--- a/src/jsifier.js
+++ b/src/jsifier.js
@@ -756,14 +756,7 @@ function JSify(data, functionsOnly, givenFunctions) {
if (func.setjmpTable && !ASM_JS) {
ret += ' } catch(e) { if (!e.longjmp || !(e.id in mySetjmpIds)) throw(e); setjmpTable[setjmpLabels[e.id]](e.value) }';
}
- if (ASM_JS && func.returnType !== 'void') {
- // Add a return
- if (func.returnType in Runtime.FLOAT_TYPES) {
- ret += ' return +0;\n';
- } else {
- ret += ' return 0;\n';
- }
- }
+ if (ASM_JS && func.returnType !== 'void') ret += ' return ' + asmInitializer(func.returnType) + ';\n'; // Add a return
} else {
ret += (SHOW_LABELS ? indent + '/* ' + block.entries[0] + ' */' : '') + '\n' + getLabelLines(block.labels[0]);
}
@@ -833,11 +826,7 @@ function JSify(data, functionsOnly, givenFunctions) {
var lastReturn = func.JS.lastIndexOf('return ');
if ((lastCurly < 0 && lastReturn < 0) || // no control flow, no return
(lastCurly >= 0 && lastReturn < lastCurly)) { // control flow, no return past last join
- if (func.returnType in Runtime.FLOAT_TYPES) {
- func.JS += ' return +0;\n';
- } else {
- func.JS += ' return 0;\n';
- }
+ func.JS += ' return ' + asmInitializer(func.returnType) + ';\n';
}
}
func.JS += '}\n';
@@ -1337,7 +1326,7 @@ function JSify(data, functionsOnly, givenFunctions) {
if (isNumber(item.ident)) {
// Direct read from a memory address; this may be an intentional segfault, if not, it is a bug in the source
if (ASM_JS) {
- return asmCoercion('abort(' + item.ident + ')', item.type);
+ return asmFFICoercion('abort(' + item.ident + ')', item.type);
} else {
item.assignTo = null;
return 'throw "fault on read from ' + item.ident + '";';
@@ -1514,8 +1503,10 @@ function JSify(data, functionsOnly, givenFunctions) {
args = args.map(function(arg, i) { return indexizeFunctions(arg, argsTypes[i]) });
if (ASM_JS) {
- if (shortident in Functions.libraryFunctions || simpleIdent in Functions.libraryFunctions || byPointerForced || invoke || extCall || funcData.setjmpTable) {
- args = args.map(function(arg, i) { return asmCoercion(arg, argsTypes[i]) });
+ var ffiCall = (shortident in Functions.libraryFunctions || simpleIdent in Functions.libraryFunctions || byPointerForced || invoke || extCall || funcData.setjmpTable) &&
+ !(simpleIdent in JS_MATH_BUILTINS);
+ if (ffiCall) {
+ args = args.map(function(arg, i) { return asmCoercion(arg, ensureValidFFIType(argsTypes[i])) });
} else {
args = args.map(function(arg, i) { return asmEnsureFloat(arg, argsTypes[i]) });
}
@@ -1592,7 +1583,7 @@ function JSify(data, functionsOnly, givenFunctions) {
returnType = getReturnType(type);
if (callIdent in Functions.implementedFunctions) {
// LLVM sometimes bitcasts for no reason. We must call using the exact same type as the actual function is generated as
- var trueType = Functions.getSignatureReturnType(Functions.implementedFunctions[callIdent]);
+ var trueType = Functions.getSignatureType(Functions.implementedFunctions[callIdent][0]);
if (trueType !== returnType && !isIdenticallyImplemented(trueType, returnType)) {
if (VERBOSE) warnOnce('Fixing function call based on return type from signature, on ' + [callIdent, returnType, trueType]);
returnType = trueType;
@@ -1628,7 +1619,11 @@ function JSify(data, functionsOnly, givenFunctions) {
var ret = callIdent + '(' + args.join(',') + ')';
if (ASM_JS) { // TODO: do only when needed (library functions and Math.*?) XXX && simpleIdent in Functions.libraryFunctions) {
- ret = asmCoercion(ret, returnType);
+ if (ffiCall) {
+ ret = asmFFICoercion(ret, returnType);
+ } else {
+ ret = asmCoercion(ret, returnType);
+ }
if (simpleIdent == 'abort' && funcData.returnType != 'void') {
ret += '; return ' + asmCoercion('0', funcData.returnType); // special case: abort() can happen without return, breaking the return type of asm functions. ensure a return
}
diff --git a/src/library.js b/src/library.js
index ccf62b27..b25e48ed 100644
--- a/src/library.js
+++ b/src/library.js
@@ -847,10 +847,7 @@ LibraryManager.library = {
___setErrNo(ERRNO_CODES.ERANGE);
return 0;
} else {
- for (var i = 0; i < cwd.length; i++) {
- {{{ makeSetValue('buf', 'i', 'cwd.charCodeAt(i)', 'i8') }}}
- }
- {{{ makeSetValue('buf', 'i', '0', 'i8') }}}
+ writeAsciiToMemory(cwd, buf);
return buf;
}
},
@@ -1293,10 +1290,7 @@ LibraryManager.library = {
if (namesize < ret.length + 1) {
return ___setErrNo(ERRNO_CODES.ERANGE);
} else {
- for (var i = 0; i < ret.length; i++) {
- {{{ makeSetValue('name', 'i', 'ret.charCodeAt(i)', 'i8') }}}
- }
- {{{ makeSetValue('name', 'i', '0', 'i8') }}}
+ writeAsciiToMemory(ret, name);
return 0;
}
},
@@ -2699,10 +2693,7 @@ LibraryManager.library = {
var result = dir + '/' + name;
if (!_tmpnam.buffer) _tmpnam.buffer = _malloc(256);
if (!s) s = _tmpnam.buffer;
- for (var i = 0; i < result.length; i++) {
- {{{ makeSetValue('s', 'i', 'result.charCodeAt(i)', 'i8') }}};
- }
- {{{ makeSetValue('s', 'i', '0', 'i8') }}};
+ writeAsciiToMemory(result, s);
return s;
},
tempnam__deps: ['tmpnam'],
@@ -3343,10 +3334,7 @@ LibraryManager.library = {
var ptrSize = {{{ Runtime.getNativeTypeSize('i8*') }}};
for (var i = 0; i < strings.length; i++) {
var line = strings[i];
- for (var j = 0; j < line.length; j++) {
- {{{ makeSetValue('poolPtr', 'j', 'line.charCodeAt(j)', 'i8') }}};
- }
- {{{ makeSetValue('poolPtr', 'j', '0', 'i8') }}};
+ writeAsciiToMemory(line, poolPtr);
{{{ makeSetValue('envPtr', 'i * ptrSize', 'poolPtr', 'i8*') }}};
poolPtr += line.length + 1;
}
@@ -3976,10 +3964,7 @@ LibraryManager.library = {
return ___setErrNo(ERRNO_CODES.ERANGE);
} else {
var msg = ERRNO_MESSAGES[errnum];
- for (var i = 0; i < msg.length; i++) {
- {{{ makeSetValue('strerrbuf', 'i', 'msg.charCodeAt(i)', 'i8') }}}
- }
- {{{ makeSetValue('strerrbuf', 'i', 0, 'i8') }}}
+ writeAsciiToMemory(msg, strerrbuf);
return 0;
}
} else {
@@ -5067,10 +5052,7 @@ LibraryManager.library = {
var layout = {{{ JSON.stringify(C_STRUCTS.utsname) }}};
function copyString(element, value) {
var offset = layout[element];
- for (var i = 0; i < value.length; i++) {
- {{{ makeSetValue('name', 'offset + i', 'value.charCodeAt(i)', 'i8') }}}
- }
- {{{ makeSetValue('name', 'offset + i', '0', 'i8') }}}
+ writeAsciiToMemory(value, name + offset);
}
if (name === 0) {
return -1;
@@ -6131,8 +6113,10 @@ LibraryManager.library = {
// int nanosleep(const struct timespec *rqtp, struct timespec *rmtp);
var seconds = {{{ makeGetValue('rqtp', C_STRUCTS.timespec.tv_sec, 'i32') }}};
var nanoseconds = {{{ makeGetValue('rqtp', C_STRUCTS.timespec.tv_nsec, 'i32') }}};
- {{{ makeSetValue('rmtp', C_STRUCTS.timespec.tv_sec, '0', 'i32') }}}
- {{{ makeSetValue('rmtp', C_STRUCTS.timespec.tv_nsec, '0', 'i32') }}}
+ if (rmtp !== 0) {
+ {{{ makeSetValue('rmtp', C_STRUCTS.timespec.tv_sec, '0', 'i32') }}}
+ {{{ makeSetValue('rmtp', C_STRUCTS.timespec.tv_nsec, '0', 'i32') }}}
+ }
return _usleep((seconds * 1e6) + (nanoseconds / 1000));
},
// TODO: Implement these for real.
@@ -6572,10 +6556,7 @@ LibraryManager.library = {
var me = _nl_langinfo;
if (!me.ret) me.ret = _malloc(32);
- for (var i = 0; i < result.length; i++) {
- {{{ makeSetValue('me.ret', 'i', 'result.charCodeAt(i)', 'i8') }}}
- }
- {{{ makeSetValue('me.ret', 'i', '0', 'i8') }}}
+ writeAsciiToMemory(result, me.ret);
return me.ret;
},
diff --git a/src/library_gl.js b/src/library_gl.js
index ecb72f0f..4b6ea579 100644
--- a/src/library_gl.js
+++ b/src/library_gl.js
@@ -530,7 +530,11 @@ var LibraryGL = {
ret = allocate(intArrayFromString('OpenGL ES GLSL 1.00 (WebGL)'), 'i8', ALLOC_NORMAL);
break;
default:
- throw 'Failure: Invalid glGetString value: ' + name_;
+ GL.recordError(0x0500/*GL_INVALID_ENUM*/);
+#if GL_ASSERTIONS
+ Module.printErr('GL_INVALID_ENUM in glGetString: Unknown parameter ' + name_ + '!');
+#endif
+ return 0;
}
GL.stringCache[name_] = ret;
return ret;
@@ -561,7 +565,11 @@ var LibraryGL = {
{{{ makeSetValue('p', '0', 'result ? 1 : 0', 'i8') }}};
break;
case "string":
- throw 'Native code calling glGetIntegerv(' + name_ + ') on a name which returns a string!';
+ GL.recordError(0x0500/*GL_INVALID_ENUM*/);
+#if GL_ASSERTIONS
+ Module.printErr('GL_INVALID_ENUM in glGetIntegerv: Native code calling glGetIntegerv(' + name_ + ') on a name which returns a string!');
+#endif
+ return;
case "object":
if (result === null) {
{{{ makeSetValue('p', '0', '0', 'i32') }}};
@@ -583,13 +591,19 @@ var LibraryGL = {
} else if (result instanceof WebGLTexture) {
{{{ makeSetValue('p', '0', 'result.name | 0', 'i32') }}};
} else {
- throw 'Unknown object returned from WebGL getParameter';
+ GL.recordError(0x0500/*GL_INVALID_ENUM*/);
+#if GL_ASSERTIONS
+ Module.printErr('GL_INVALID_ENUM in glGetIntegerv: Unknown object returned from WebGL getParameter(' + name_ + ')!');
+#endif
+ return;
}
break;
- case "undefined":
- throw 'Native code calling glGetIntegerv(' + name_ + ') and it returns undefined';
default:
- throw 'Why did we hit the default case?';
+ GL.recordError(0x0500/*GL_INVALID_ENUM*/);
+#if GL_ASSERTIONS
+ Module.printErr('GL_INVALID_ENUM in glGetIntegerv: Native code calling glGetIntegerv(' + name_ + ') and it returns ' + result + ' of type ' + typeof(result) + '!');
+#endif
+ return;
}
},
@@ -607,7 +621,11 @@ var LibraryGL = {
{{{ makeSetValue('p', '0', '0', 'float') }}};
case "object":
if (result === null) {
- throw 'Native code calling glGetFloatv(' + name_ + ') and it returns null';
+ GL.recordError(0x0500/*GL_INVALID_ENUM*/);
+#if GL_ASSERTIONS
+ Module.printErr('GL_INVALID_ENUM in glGetFloatv: Native code calling glGetFloatv(' + name_ + ') and it returns null!');
+#endif
+ return;
} else if (result instanceof Float32Array ||
result instanceof Uint32Array ||
result instanceof Int32Array ||
@@ -626,13 +644,19 @@ var LibraryGL = {
} else if (result instanceof WebGLTexture) {
{{{ makeSetValue('p', '0', 'result.name | 0', 'float') }}};
} else {
- throw 'Unknown object returned from WebGL getParameter';
+ GL.recordError(0x0500/*GL_INVALID_ENUM*/);
+#if GL_ASSERTIONS
+ Module.printErr('GL_INVALID_ENUM in glGetFloatv: Native code calling glGetFloatv(' + name_ + ') and it returns ' + result + ' of type ' + typeof(result) + '!');
+#endif
+ return;
}
break;
- case "undefined":
- throw 'Native code calling glGetFloatv(' + name_ + ') and it returns undefined';
default:
- throw 'Why did we hit the default case?';
+ GL.recordError(0x0500/*GL_INVALID_ENUM*/);
+#if GL_ASSERTIONS
+ Module.printErr('GL_INVALID_ENUM in glGetFloatv: Native code calling glGetFloatv(' + name_ + ') and it returns ' + result + ' of type ' + typeof(result) + '!');
+#endif
+ return;
}
},
@@ -647,7 +671,11 @@ var LibraryGL = {
{{{ makeSetValue('p', '0', 'result != 0', 'i8') }}};
break;
case "string":
- throw 'Native code calling glGetBooleanv(' + name_ + ') on a name which returns a string!';
+ GL.recordError(0x0500/*GL_INVALID_ENUM*/);
+#if GL_ASSERTIONS
+ Module.printErr('GL_INVALID_ENUM in glGetBooleanv: Native code calling glGetBooleanv(' + name_ + ') on a name which returns a string!');
+#endif
+ return;
case "object":
if (result === null) {
{{{ makeSetValue('p', '0', '0', 'i8') }}};
@@ -665,13 +693,19 @@ var LibraryGL = {
result instanceof WebGLTexture) {
{{{ makeSetValue('p', '0', '1', 'i8') }}}; // non-zero ID is always 1!
} else {
- throw 'Unknown object returned from WebGL getParameter';
+ GL.recordError(0x0500/*GL_INVALID_ENUM*/);
+#if GL_ASSERTIONS
+ Module.printErr('GL_INVALID_ENUM in glGetBooleanv: Unknown object returned from WebGL getParameter(' + name_ + ')!');
+#endif
+ return;
}
break;
- case "undefined":
- throw 'Unknown object returned from WebGL getParameter';
default:
- throw 'Why did we hit the default case?';
+ GL.recordError(0x0500/*GL_INVALID_ENUM*/);
+#if GL_ASSERTIONS
+ Module.printErr('GL_INVALID_ENUM in glGetBooleanv: Native code calling glGetBooleanv(' + name_ + ') and it returns ' + result + ' of type ' + typeof(result) + '!');
+#endif
+ return;
}
},
@@ -759,7 +793,12 @@ var LibraryGL = {
case 0x1908 /* GL_RGBA */:
sizePerPixel = 4;
break;
- default: throw 'unsupported glReadPixels format';
+ default:
+ GL.recordError(0x0500/*GL_INVALID_ENUM*/);
+#if GL_ASSERTIONS
+ Module.printErr('GL_INVALID_ENUM in glReadPixels: Unsupported format ' + format + '!');
+#endif
+ return;
}
var totalSize = width*height*sizePerPixel;
Module.ctx.readPixels(x, y, width, height, format, type, HEAPU8.subarray(pixels, pixels + totalSize));
@@ -2191,7 +2230,12 @@ var LibraryGL = {
attribute = GLImmediate.clientAttributes[GLImmediate.COLOR]; break;
case 0x8092: // GL_TEXTURE_COORD_ARRAY_POINTER
attribute = GLImmediate.clientAttributes[GLImmediate.TEXTURE0 + GLImmediate.clientActiveTexture]; break;
- default: throw 'TODO: glGetPointerv for ' + name;
+ default:
+ GL.recordError(0x0500/*GL_INVALID_ENUM*/);
+#if GL_ASSERTIONS
+ Module.printErr('GL_INVALID_ENUM in glGetPointerv: Unsupported name ' + name + '!');
+#endif
+ return;
}
{{{ makeSetValue('p', '0', 'attribute ? attribute.pointer : 0', 'i32') }}};
},
diff --git a/src/library_sdl.js b/src/library_sdl.js
index 5b43b7ab..c46364ff 100644
--- a/src/library_sdl.js
+++ b/src/library_sdl.js
@@ -75,6 +75,7 @@ var LibrarySDL = {
textInput: false,
startTime: null,
+ initFlags: 0, // The flags passed to SDL_Init
buttonState: 0,
modState: 0,
DOMButtons: [0, 0, 0],
@@ -639,6 +640,21 @@ var LibrarySDL = {
{{{ makeSetValue('ptr', C_STRUCTS.SDL_ResizeEvent.h, 'event.h', 'i32') }}};
break;
}
+ case 'joystick_button_up': case 'joystick_button_down': {
+ var state = event.type === 'joystick_button_up' ? 0 : 1;
+ {{{ makeSetValue('ptr', C_STRUCTS.SDL_JoyButtonEvent.type, 'SDL.DOMEventToSDLEvent[event.type]', 'i32') }}};
+ {{{ makeSetValue('ptr', C_STRUCTS.SDL_JoyButtonEvent.which, 'event.index', 'i8') }}};
+ {{{ makeSetValue('ptr', C_STRUCTS.SDL_JoyButtonEvent.button, 'event.button', 'i8') }}};
+ {{{ makeSetValue('ptr', C_STRUCTS.SDL_JoyButtonEvent.state, 'state', 'i8') }}};
+ break;
+ }
+ case 'joystick_axis_motion': {
+ {{{ makeSetValue('ptr', C_STRUCTS.SDL_JoyAxisEvent.type, 'SDL.DOMEventToSDLEvent[event.type]', 'i32') }}};
+ {{{ makeSetValue('ptr', C_STRUCTS.SDL_JoyAxisEvent.which, 'event.index', 'i8') }}};
+ {{{ makeSetValue('ptr', C_STRUCTS.SDL_JoyAxisEvent.axis, 'event.axis', 'i8') }}};
+ {{{ makeSetValue('ptr', C_STRUCTS.SDL_JoyAxisEvent.value, 'SDL.joystickAxisValueConversion(event.value)', 'i32') }}};
+ break;
+ }
default: throw 'Unhandled SDL event: ' + event.type;
}
},
@@ -695,7 +711,109 @@ var LibrarySDL = {
for (var i = 0; i < num; i++) {
console.log(' diagonal ' + i + ':' + [data[i*surfData.width*4 + i*4 + 0], data[i*surfData.width*4 + i*4 + 1], data[i*surfData.width*4 + i*4 + 2], data[i*surfData.width*4 + i*4 + 3]]);
}
- }
+ },
+
+ // Joystick helper methods and state
+
+ joystickEventState: 0,
+ lastJoystickState: {}, // Map from SDL_Joystick* to their last known state. Required to determine if a change has occurred.
+ // Maps Joystick names to pointers. Allows us to avoid reallocating memory for
+ // joystick names each time this function is called.
+ joystickNamePool: {},
+ recordJoystickState: function(joystick, state) {
+ // Standardize button state.
+ var buttons = new Array(state.buttons.length);
+ for (var i = 0; i < state.buttons.length; i++) {
+ buttons[i] = SDL.getJoystickButtonState(state.buttons[i]);
+ }
+
+ SDL.lastJoystickState[joystick] = {
+ buttons: buttons,
+ axes: state.axes.slice(0),
+ timestamp: state.timestamp,
+ index: state.index,
+ id: state.id
+ };
+ },
+ // Retrieves the button state of the given gamepad button.
+ // Abstracts away implementation differences.
+ // Returns 'true' if pressed, 'false' otherwise.
+ getJoystickButtonState: function(button) {
+ if (typeof button === 'object') {
+ // Current gamepad API editor's draft (Firefox Nightly)
+ // https://dvcs.w3.org/hg/gamepad/raw-file/default/gamepad.html#idl-def-GamepadButton
+ return button.pressed;
+ } else {
+ // Current gamepad API working draft (Firefox / Chrome Stable)
+ // http://www.w3.org/TR/2012/WD-gamepad-20120529/#gamepad-interface
+ return button > 0;
+ }
+ },
+ // Queries for and inserts controller events into the SDL queue.
+ queryJoysticks: function() {
+ for (var joystick in SDL.lastJoystickState) {
+ var state = SDL.getGamepad(joystick - 1);
+ var prevState = SDL.lastJoystickState[joystick];
+ // Check only if the timestamp has differed.
+ // NOTE: Timestamp is not available in Firefox.
+ if (typeof state.timestamp !== 'number' || state.timestamp !== prevState.timestamp) {
+ var i;
+ for (i = 0; i < state.buttons.length; i++) {
+ var buttonState = SDL.getJoystickButtonState(state.buttons[i]);
+ // NOTE: The previous state already has a boolean representation of
+ // its button, so no need to standardize its button state here.
+ if (buttonState !== prevState.buttons[i]) {
+ // Insert button-press event.
+ SDL.events.push({
+ type: buttonState ? 'joystick_button_down' : 'joystick_button_up',
+ joystick: joystick,
+ index: joystick - 1,
+ button: i
+ });
+ }
+ }
+ for (i = 0; i < state.axes.length; i++) {
+ if (state.axes[i] !== prevState.axes[i]) {
+ // Insert axes-change event.
+ SDL.events.push({
+ type: 'joystick_axis_motion',
+ joystick: joystick,
+ index: joystick - 1,
+ axis: i,
+ value: state.axes[i]
+ });
+ }
+ }
+
+ SDL.recordJoystickState(joystick, state);
+ }
+ }
+ },
+ // Converts the double-based browser axis value [-1, 1] into SDL's 16-bit
+ // value [-32768, 32767]
+ joystickAxisValueConversion: function(value) {
+ // Ensures that 0 is 0, 1 is 32767, and -1 is 32768.
+ return Math.ceil(((value+1) * 32767.5) - 32768);
+ },
+
+ getGamepads: function() {
+ var fcn = navigator.getGamepads || navigator.webkitGamepads || navigator.mozGamepads || navigator.gamepads || navigator.webkitGetGamepads;
+ if (fcn !== undefined) {
+ // The function must be applied on the navigator object.
+ return fcn.apply(navigator);
+ } else {
+ return [];
+ }
+ },
+
+ // Helper function: Returns the gamepad if available, or null if not.
+ getGamepad: function(deviceIndex) {
+ var gamepads = SDL.getGamepads();
+ if (gamepads.length > deviceIndex && deviceIndex >= 0) {
+ return gamepads[deviceIndex];
+ }
+ return null;
+ },
},
SDL_Linked_Version: function() {
@@ -708,8 +826,10 @@ var LibrarySDL = {
return SDL.version;
},
- SDL_Init: function(what) {
+ SDL_Init: function(initFlags) {
SDL.startTime = Date.now();
+ SDL.initFlags = initFlags;
+
// capture all key events. we just keep down and up, but also capture press to prevent default actions
if (!Module['doNotCaptureKeyboard']) {
document.addEventListener("keydown", SDL.receiveEvent);
@@ -718,6 +838,15 @@ var LibrarySDL = {
window.addEventListener("blur", SDL.receiveEvent);
document.addEventListener("visibilitychange", SDL.receiveEvent);
}
+
+ if (initFlags & 0x200) {
+ // SDL_INIT_JOYSTICK
+ // Firefox will not give us Joystick data unless we register this NOP
+ // callback.
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=936104
+ addEventListener("gamepadconnected", function() {});
+ }
+
window.addEventListener("unload", SDL.receiveEvent);
SDL.keyboardState = _malloc(0x10000); // Our SDL needs 512, but 64K is safe for older SDLs
_memset(SDL.keyboardState, 0, 0x10000);
@@ -730,6 +859,12 @@ var LibrarySDL = {
SDL.DOMEventToSDLEvent['mousemove'] = 0x400 /* SDL_MOUSEMOTION */;
SDL.DOMEventToSDLEvent['unload'] = 0x100 /* SDL_QUIT */;
SDL.DOMEventToSDLEvent['resize'] = 0x7001 /* SDL_VIDEORESIZE/SDL_EVENT_COMPAT2 */;
+ // These are not technically DOM events; the HTML gamepad API is poll-based.
+ // However, we define them here, as the rest of the SDL code assumes that
+ // all SDL events originate as DOM events.
+ SDL.DOMEventToSDLEvent['joystick_axis_motion'] = 0x600 /* SDL_JOYAXISMOTION */;
+ SDL.DOMEventToSDLEvent['joystick_button_down'] = 0x603 /* SDL_JOYBUTTONDOWN */;
+ SDL.DOMEventToSDLEvent['joystick_button_up'] = 0x604 /* SDL_JOYBUTTONUP */;
return 0; // success
},
@@ -1189,6 +1324,11 @@ var LibrarySDL = {
},
SDL_PollEvent: function(ptr) {
+ if (SDL.initFlags & 0x200 && SDL.joystickEventState) {
+ // If SDL_INIT_JOYSTICK was supplied AND the joystick system is configured
+ // to automatically query for events, query for joystick events.
+ SDL.queryJoysticks();
+ }
if (SDL.events.length === 0) return 0;
if (ptr) {
SDL.makeCEvent(SDL.events.shift(), ptr);
@@ -2375,37 +2515,103 @@ var LibrarySDL = {
// Joysticks
- SDL_NumJoysticks: function() { return 0; },
+ SDL_NumJoysticks: function() {
+ var count = 0;
+ var gamepads = SDL.getGamepads();
+ // The length is not the number of gamepads; check which ones are defined.
+ for (var i = 0; i < gamepads.length; i++) {
+ if (gamepads[i] !== undefined) count++;
+ }
+ return count;
+ },
- SDL_JoystickName: function(deviceIndex) { return 0; },
+ SDL_JoystickName: function(deviceIndex) {
+ var gamepad = SDL.getGamepad(deviceIndex);
+ if (gamepad) {
+ var name = gamepad.id;
+ if (SDL.joystickNamePool.hasOwnProperty(name)) {
+ return SDL.joystickNamePool[name];
+ }
+ return SDL.joystickNamePool[name] = allocate(intArrayFromString(name), 'i8', ALLOC_NORMAL);
+ }
+ return 0;
+ },
- SDL_JoystickOpen: function(deviceIndex) { return 0; },
+ SDL_JoystickOpen: function(deviceIndex) {
+ var gamepad = SDL.getGamepad(deviceIndex);
+ if (gamepad) {
+ // Use this as a unique 'pointer' for this joystick.
+ var joystick = deviceIndex+1;
+ SDL.recordJoystickState(joystick, gamepad);
+ return joystick;
+ }
+ return 0;
+ },
- SDL_JoystickOpened: function(deviceIndex) { return 0; },
+ SDL_JoystickOpened: function(deviceIndex) {
+ return SDL.lastJoystickState.hasOwnProperty(deviceIndex+1) ? 1 : 0;
+ },
- SDL_JoystickIndex: function(joystick) { return 0; },
+ SDL_JoystickIndex: function(joystick) {
+ // joystick pointers are simply the deviceIndex+1.
+ return joystick - 1;
+ },
- SDL_JoystickNumAxes: function(joystick) { return 0; },
+ SDL_JoystickNumAxes: function(joystick) {
+ var gamepad = SDL.getGamepad(joystick - 1);
+ if (gamepad) {
+ return gamepad.axes.length;
+ }
+ return 0;
+ },
SDL_JoystickNumBalls: function(joystick) { return 0; },
SDL_JoystickNumHats: function(joystick) { return 0; },
- SDL_JoystickNumButtons: function(joystick) { return 0; },
+ SDL_JoystickNumButtons: function(joystick) {
+ var gamepad = SDL.getGamepad(joystick - 1);
+ if (gamepad) {
+ return gamepad.buttons.length;
+ }
+ return 0;
+ },
- SDL_JoystickUpdate: function() {},
+ SDL_JoystickUpdate: function() {
+ SDL.queryJoysticks();
+ },
- SDL_JoystickEventState: function(state) { return 0; },
+ SDL_JoystickEventState: function(state) {
+ if (state < 0) {
+ // SDL_QUERY: Return current state.
+ return SDL.joystickEventState;
+ }
+ return SDL.joystickEventState = state;
+ },
- SDL_JoystickGetAxis: function(joystick, axis) { return 0; },
+ SDL_JoystickGetAxis: function(joystick, axis) {
+ var gamepad = SDL.getGamepad(joystick - 1);
+ if (gamepad && gamepad.axes.length > axis) {
+ return SDL.joystickAxisValueConversion(gamepad.axes[axis]);
+ }
+ return 0;
+ },
SDL_JoystickGetHat: function(joystick, hat) { return 0; },
SDL_JoystickGetBall: function(joystick, ball, dxptr, dyptr) { return -1; },
- SDL_JoystickGetButton: function(joystick, button) { return 0; },
+ SDL_JoystickGetButton: function(joystick, button) {
+ var gamepad = SDL.getGamepad(joystick - 1);
+ if (gamepad && gamepad.buttons.length > button) {
+ return SDL.getJoystickButtonState(gamepad.buttons[button]) ? 1 : 0;
+ }
+ return 0;
+ },
- SDL_JoystickClose: function(joystick) {},
+ SDL_JoystickClose: function(joystick) {
+ delete SDL.lastJoystickState[joystick];
+ },
// Misc
diff --git a/src/modules.js b/src/modules.js
index 854575e0..13cca977 100644
--- a/src/modules.js
+++ b/src/modules.js
@@ -18,7 +18,7 @@ var LLVM = {
PHI_REACHERS: set('branch', 'switch', 'invoke', 'indirectbr'),
EXTENDS: set('sext', 'zext'),
COMPS: set('icmp', 'fcmp'),
- CONVERSIONS: set('inttoptr', 'ptrtoint', 'uitofp', 'sitofp', 'fptosi', 'fptoui'),
+ CONVERSIONS: set('inttoptr', 'ptrtoint', 'uitofp', 'sitofp', 'fptosi', 'fptoui', 'fpext', 'fptrunc'),
INTRINSICS_32: set('_llvm_memcpy_p0i8_p0i8_i64', '_llvm_memmove_p0i8_p0i8_i64', '_llvm_memset_p0i8_i64'), // intrinsics that need args converted to i32 in USE_TYPED_ARRAYS == 2
};
LLVM.GLOBAL_MODIFIERS = set(keys(LLVM.LINKAGES).concat(['constant', 'global', 'hidden']));
@@ -253,13 +253,32 @@ var Functions = {
aliases: {}, // in shared modules (MAIN_MODULE or SHARED_MODULE), a list of aliases for functions that have them
+ getSignatureLetter: function(type) {
+ switch(type) {
+ case 'float': return 'f';
+ case 'double': return 'd';
+ case 'void': return 'v';
+ default: return 'i';
+ }
+ },
+
+ getSignatureType: function(letter) {
+ switch(letter) {
+ case 'v': return 'void';
+ case 'i': return 'i32';
+ case 'f': return 'float';
+ case 'd': return 'double';
+ default: throw 'what is this sig? ' + sig;
+ }
+ },
+
getSignature: function(returnType, argTypes, hasVarArgs) {
- var sig = returnType == 'void' ? 'v' : (isIntImplemented(returnType) ? 'i' : 'f');
+ var sig = Functions.getSignatureLetter(returnType);
for (var i = 0; i < argTypes.length; i++) {
var type = argTypes[i];
if (!type) break; // varargs
if (type in Runtime.FLOAT_TYPES) {
- sig += 'f';
+ sig += Functions.getSignatureLetter(type);
} else {
var chunks = getNumIntChunks(type);
for (var j = 0; j < chunks; j++) sig += 'i';
@@ -269,15 +288,6 @@ var Functions = {
return sig;
},
- getSignatureReturnType: function(sig) {
- switch(sig[0]) {
- case 'v': return 'void';
- case 'i': return 'i32';
- case 'f': return 'double';
- default: throw 'what is this sig? ' + sig;
- }
- },
-
// Mark a function as needing indexing. Python will coordinate them all
getIndex: function(ident, sig) {
var ret;
@@ -350,17 +360,15 @@ var Functions = {
if (!wrapped[curr]) {
var args = '', arg_coercions = '', call = short + '(', retPre = '', retPost = '';
if (t[0] != 'v') {
- if (t[0] == 'i') {
- retPre = 'return ';
- retPost = '|0';
- } else {
- retPre = 'return +';
- }
+ var temp = asmFFICoercion('X', Functions.getSignatureType(t[0])).split('X');
+ retPre = 'return ' + temp[0];
+ retPost = temp[1];
}
for (var j = 1; j < t.length; j++) {
args += (j > 1 ? ',' : '') + 'a' + j;
- arg_coercions += 'a' + j + '=' + asmCoercion('a' + j, t[j] != 'i' ? 'float' : 'i32') + ';';
- call += (j > 1 ? ',' : '') + asmCoercion('a' + j, t[j] != 'i' ? 'float' : 'i32');
+ var type = Functions.getSignatureType(t[j]);
+ arg_coercions += 'a' + j + '=' + asmCoercion('a' + j, type) + ';';
+ call += (j > 1 ? ',' : '') + asmCoercion('a' + j, type === 'float' ? 'double' : type); // ffi arguments must be doubles if they are floats
}
call += ')';
if (short == '_setjmp') printErr('WARNING: setjmp used via a function pointer. If this is for libc setjmp (not something of your own with the same name), it will break things');
diff --git a/src/parseTools.js b/src/parseTools.js
index c4f28184..3c2eeece 100644
--- a/src/parseTools.js
+++ b/src/parseTools.js
@@ -629,6 +629,8 @@ function cleanSegment(segment) {
var MATHOPS = set(['add', 'sub', 'sdiv', 'udiv', 'mul', 'icmp', 'zext', 'urem', 'srem', 'fadd', 'fsub', 'fmul', 'fdiv', 'fcmp', 'frem', 'uitofp', 'sitofp', 'fpext', 'fptrunc', 'fptoui', 'fptosi', 'trunc', 'sext', 'select', 'shl', 'shr', 'ashl', 'ashr', 'lshr', 'lshl', 'xor', 'or', 'and', 'ptrtoint', 'inttoptr']);
+var JS_MATH_BUILTINS = set(['Math_sin', 'Math_cos', 'Math_tan', 'Math_asin', 'Math_acos', 'Math_atan', 'Math_ceil', 'Math_floor', 'Math_exp', 'Math_log', 'Math_sqrt']);
+
var PARSABLE_LLVM_FUNCTIONS = set('getelementptr', 'bitcast');
mergeInto(PARSABLE_LLVM_FUNCTIONS, MATHOPS);
@@ -788,8 +790,8 @@ function splitI64(value, floatConversion) {
var high = makeInlineCalculation(
asmCoercion('Math_abs(VALUE)', 'double') + ' >= ' + asmEnsureFloat('1', 'double') + ' ? ' +
'(VALUE > ' + asmEnsureFloat('0', 'double') + ' ? ' +
- asmCoercion('Math_min(' + asmCoercion('Math_floor((VALUE)/' + asmEnsureFloat(4294967296, 'float') + ')', 'double') + ', ' + asmEnsureFloat(4294967295, 'float') + ')', 'i32') + '>>>0' +
- ' : ' + asmFloatToInt(asmCoercion('Math_ceil((VALUE - +((' + asmFloatToInt('VALUE') + ')>>>0))/' + asmEnsureFloat(4294967296, 'float') + ')', 'double')) + '>>>0' +
+ asmCoercion('Math_min(' + asmCoercion('Math_floor((VALUE)/' + asmEnsureFloat(4294967296, 'double') + ')', 'double') + ', ' + asmEnsureFloat(4294967295, 'double') + ')', 'i32') + '>>>0' +
+ ' : ' + asmFloatToInt(asmCoercion('Math_ceil((VALUE - +((' + asmFloatToInt('VALUE') + ')>>>0))/' + asmEnsureFloat(4294967296, 'double') + ')', 'double')) + '>>>0' +
')' +
' : 0',
value,
@@ -981,6 +983,12 @@ function parseLLVMString(str) {
return ret;
}
+function expandLLVMString(str) {
+ return str.replace(/\\../g, function(m) {
+ return String.fromCharCode(parseInt(m.substr(1), '16'));
+ });
+}
+
function getLabelIds(labels) {
return labels.map(function(label) { return label.ident });
}
@@ -1161,32 +1169,37 @@ function makeVarDef(js) {
return js;
}
+function ensureDot(value) {
+ value = value.toString();
+ // if already dotted, or Infinity or NaN, nothing to do here
+ // if smaller than 1 and running js opts, we always need to force a coercion (0.001 will turn into 1e-3, which has no .)
+ if ((value.indexOf('.') >= 0 || /[IN]/.test(value)) && (!RUNNING_JS_OPTS || Math.abs(value) >= 1)) return value;
+ if (RUNNING_JS_OPTS) return '(+' + value + ')'; // JS optimizer will run, we must do +x, and it will be corrected later
+ var e = value.indexOf('e');
+ if (e < 0) return value + '.0';
+ return value.substr(0, e) + '.0' + value.substr(e);
+}
+
function asmEnsureFloat(value, type) { // ensures that a float type has either 5.5 (clearly a float) or +5 (float due to asm coercion)
if (!ASM_JS) return value;
- // coerce if missing a '.', or if smaller than 1, so could be 1e-5 which has no .
- if (type in Runtime.FLOAT_TYPES && isNumber(value) && (value.toString().indexOf('.') < 0 || Math.abs(value) < 1)) {
- if (RUNNING_JS_OPTS) {
- return '(+' + value + ')'; // JS optimizer will run, we must do +x, and it will be corrected later
- } else {
- // ensure a .
- value = value.toString();
- if (value.indexOf('.') >= 0 || /[IN]/.test(value)) return value; // if already dotted, or Infinity or NaN, nothing to do here
- var e = value.indexOf('e');
- if (e < 0) return value + '.0';
- return value.substr(0, e) + '.0' + value.substr(e);
- }
+ if (!isNumber(value)) return value;
+ if (PRECISE_F32 && type === 'float') {
+ // normally ok to just emit Math_fround(0), but if the constant is large we may need a .0 (if it can't fit in an int)
+ if (value == 0) return 'Math_fround(0)';
+ value = ensureDot(value);
+ return 'Math_fround(' + value + ')';
+ }
+ if (type in Runtime.FLOAT_TYPES) {
+ return ensureDot(value);
} else {
return value;
}
}
-function asmInitializer(type, impl) {
+function asmInitializer(type) {
if (type in Runtime.FLOAT_TYPES) {
- if (RUNNING_JS_OPTS) {
- return '+0';
- } else {
- return '.0';
- }
+ if (PRECISE_F32 && type === 'float') return 'Math_fround(0)';
+ return RUNNING_JS_OPTS ? '+0' : '.0';
} else {
return '0';
}