summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/analyzer.js2
-rw-r--r--src/compiler.js38
-rw-r--r--src/intertyper.js30
-rw-r--r--src/jsifier.js82
-rw-r--r--src/library.js290
-rw-r--r--src/library_browser.js90
-rw-r--r--src/library_egl.js90
-rw-r--r--src/library_fs.js210
-rw-r--r--src/library_gl.js426
-rw-r--r--src/library_glfw.js4
-rw-r--r--src/library_glut.js15
-rw-r--r--src/library_idbfs.js28
-rw-r--r--src/library_memfs.js106
-rw-r--r--src/library_nodefs.js12
-rw-r--r--src/library_openal.js8
-rw-r--r--src/library_path.js22
-rw-r--r--src/library_sdl.js341
-rw-r--r--src/library_sockfs.js10
-rw-r--r--src/modules.js55
-rw-r--r--src/parseTools.js211
-rw-r--r--src/preamble.js60
-rw-r--r--src/proxyClient.js2
-rw-r--r--src/proxyWorker.js20
-rw-r--r--src/relooper/Relooper.cpp15
-rw-r--r--src/runtime.js35
-rw-r--r--src/settings.js29
-rw-r--r--src/shell.html7
-rw-r--r--src/shell.js24
-rw-r--r--src/struct_info.json24
-rw-r--r--src/utility.js9
30 files changed, 1553 insertions, 742 deletions
diff --git a/src/analyzer.js b/src/analyzer.js
index 2b74a83f..253c5505 100644
--- a/src/analyzer.js
+++ b/src/analyzer.js
@@ -418,7 +418,7 @@ function analyzer(data, sidePass) {
toAdd.push({
intertype: 'value',
assignTo: element.ident,
- type: element.bits,
+ type: 'i' + element.bits,
ident: 'tempRet' + (j - 1)
});
assert(j<10); // TODO: dynamically create more than 10 tempRet-s
diff --git a/src/compiler.js b/src/compiler.js
index e9197a5d..7d768c3d 100644
--- a/src/compiler.js
+++ b/src/compiler.js
@@ -206,12 +206,12 @@ if (phase == 'pre') {
if (VERBOSE) printErr('VERBOSE is on, this generates a lot of output and can slow down compilation');
// Load struct and define information.
-try {
+//try {
var temp = JSON.parse(read(STRUCT_INFO));
-} catch(e) {
- printErr('cannot load struct info at ' + STRUCT_INFO + ' : ' + e + ', trying in current dir');
- temp = JSON.parse(read('struct_info.compiled.json'));
-}
+//} catch(e) {
+// printErr('cannot load struct info at ' + STRUCT_INFO + ' : ' + e + ', trying in current dir');
+// temp = JSON.parse(read('struct_info.compiled.json'));
+//}
C_STRUCTS = temp.structs;
C_DEFINES = temp.defines;
@@ -224,12 +224,12 @@ load('analyzer.js');
load('jsifier.js');
if (phase == 'funcs' && RELOOP) { // XXX handle !singlePhase
RelooperModule = { TOTAL_MEMORY: ceilPowerOfTwo(2*RELOOPER_BUFFER_SIZE) };
- try {
+ //try {
load(RELOOPER);
- } catch(e) {
- printErr('cannot load relooper at ' + RELOOPER + ' : ' + e + ', trying in current dir');
- load('relooper.js');
- }
+ //} catch(e) {
+ // printErr('cannot load relooper at ' + RELOOPER + ' : ' + e + ', trying in current dir');
+ // load('relooper.js');
+ //}
assert(typeof Relooper != 'undefined');
}
globalEval(processMacros(preprocess(read('runtime.js'))));
@@ -267,7 +267,7 @@ function compile(raw) {
function runPhase(currPhase) {
//printErr('// JS compiler in action, phase ' + currPhase + typeof lines + (lines === null));
phase = currPhase;
- if (phase != 'pre') {
+ if (phase != 'pre' && phase != 'glue') {
if (singlePhase) PassManager.load(read(forwardedDataFile));
if (phase == 'funcs') {
@@ -311,12 +311,18 @@ function compile(raw) {
B = new Benchmarker();
-if (ll_file) {
- if (ll_file.indexOf(String.fromCharCode(10)) == -1) {
- compile(read(ll_file));
- } else {
- compile(ll_file); // we are given raw .ll
+try {
+ if (ll_file) {
+ if (phase === 'glue') {
+ compile(';');
+ } else if (ll_file.indexOf(String.fromCharCode(10)) == -1) {
+ compile(read(ll_file));
+ } else {
+ compile(ll_file); // we are given raw .ll
+ }
}
+} catch(err) {
+ printErr('aborting from js compiler due to exception: ' + err + ' | ' + err.stack);
}
//var M = keys(tokenCacheMisses).map(function(m) { return [m, misses[m]] }).sort(function(a, b) { return a[1] - b[1] });
diff --git a/src/intertyper.js b/src/intertyper.js
index 96db6966..940c677f 100644
--- a/src/intertyper.js
+++ b/src/intertyper.js
@@ -660,9 +660,10 @@ function intertyper(lines, sidePass, baseLineNums) {
var tokensLeft = item.tokens.slice(2);
item.ident = eatLLVMIdent(tokensLeft);
if (item.ident == 'asm') {
+ warnOnce('inline JavaScript using asm() has some oddities due to how gcc asm() syntax works. use EM_ASM where possible (see emscripten.h)');
if (ASM_JS) {
Types.hasInlineJS = true;
- warnOnce('inline JavaScript (asm, EM_ASM) will cause the code to no longer fall in the asm.js subset of JavaScript, which can reduce performance - consider using emscripten_run_script');
+ warnOnce('inline JavaScript using asm() will cause the code to no longer fall in the asm.js subset of JavaScript, which can reduce performance - consider using emscripten_run_script');
}
assert(TARGET_LE32, 'inline js is only supported in le32');
// Inline assembly is just JavaScript that we paste into the code
@@ -672,15 +673,20 @@ function intertyper(lines, sidePass, baseLineNums) {
assert((item.tokens[5].text.match(/=/g) || []).length <= 1, 'we only support at most 1 exported variable from inline js: ' + item.ident);
var i = 0;
var params = [], args = [];
- splitTokenList(tokensLeft[3].tokens).map(function(element) {
- var ident = toNiceIdent(element[1].text);
- var type = element[0].text;
- params.push('$' + (i++));
- args.push(ident);
+ if (tokensLeft[3].tokens) {
+ splitTokenList(tokensLeft[3].tokens).map(function(element) {
+ var ident = toNiceIdent(element[1].text);
+ var type = element[0].text;
+ params.push('$' + (i++));
+ 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 { forward: null, ret: item, item: item };
+ return { ret: item, item: item };
}
if (item.ident.substr(-2) == '()') {
// See comment in isStructType()
@@ -703,13 +709,12 @@ function intertyper(lines, sidePass, baseLineNums) {
if (item.indent == 2) {
// standalone call - not in assign
item.standalone = true;
- return { forward: null, ret: item, item: item };
+ return { ret: item, item: item };
}
- return { forward: item, ret: null, item: item };
+ return { ret: null, item: item };
}
function callHandler(item) {
var result = makeCall.call(this, item, 'call');
- if (result.forward) this.forwardItem(result.forward, 'Reintegrator');
return result.ret;
}
function invokeHandler(item) {
@@ -719,10 +724,9 @@ function intertyper(lines, sidePass, baseLineNums) {
finalResults.push({
intertype: 'branch',
label: result.item.toLabel,
- lineNum: (result.forward ? item.parentLineNum : item.lineNum) + 0.5
+ lineNum: item.lineNum + 0.5
});
}
- if (result.forward) this.forwardItem(result.forward, 'Reintegrator');
return result.ret;
}
function atomicHandler(item) {
@@ -839,7 +843,7 @@ function intertyper(lines, sidePass, baseLineNums) {
item.variant = item.tokens[1].text;
item.tokens.splice(1, 1);
}
- if (item.tokens[1].text == 'exact') item.tokens.splice(1, 1); // TODO: Implement trap values
+ while (item.tokens[1].text in LLVM.MATHOP_IGNORABLES) item.tokens.splice(1, 1);
var segments = splitTokenList(item.tokens.slice(1));
item.params = [];
for (var i = 1; i <= 4; i++) {
diff --git a/src/jsifier.js b/src/jsifier.js
index b36e11ed..cb753e57 100644
--- a/src/jsifier.js
+++ b/src/jsifier.js
@@ -28,7 +28,7 @@ function JSify(data, functionsOnly, givenFunctions) {
if (mainPass) {
var shellFile = SHELL_FILE ? SHELL_FILE : (BUILD_AS_SHARED_LIB || SIDE_MODULE ? 'shell_sharedlib.js' : 'shell.js');
- if (phase == 'pre') {
+ if (phase == 'pre' || phase == 'glue') {
// We will start to print out the data, but must do so carefully - we are
// dealing with potentially *huge* strings. Convenient replacements and
// manipulations may create in-memory copies, and we may OOM.
@@ -72,7 +72,7 @@ function JSify(data, functionsOnly, givenFunctions) {
LibraryManager.load();
//B.stop('jsifier-libload');
- if (phase == 'pre') {
+ if (phase == 'pre' || phase == 'glue') {
var libFuncsToInclude;
if (INCLUDE_FULL_LIBRARY) {
assert(!(BUILD_AS_SHARED_LIB || SIDE_MODULE), 'Cannot have both INCLUDE_FULL_LIBRARY and BUILD_AS_SHARED_LIB/SIDE_MODULE set.')
@@ -474,7 +474,7 @@ function JSify(data, functionsOnly, givenFunctions) {
}
}
if (SIDE_MODULE) return ';'; // we import into the side module js library stuff from the outside parent
- if ((!ASM_JS || phase == 'pre') &&
+ if ((!ASM_JS || phase == 'pre' || phase == 'glue') &&
(EXPORT_ALL || (ident in EXPORTED_FUNCTIONS))) {
contentText += '\nModule["' + ident + '"] = ' + ident + ';';
}
@@ -490,10 +490,19 @@ function JSify(data, functionsOnly, givenFunctions) {
} else {
// If this is not linkable, anything not in the library is definitely missing
var cancel = false;
+ if (item.ident in DEAD_FUNCTIONS) {
+ if (LibraryManager.library[shortident + '__asm']) {
+ warn('cannot kill asm library function ' + item.ident);
+ } else {
+ LibraryManager.library[shortident] = new Function("Module['printErr']('dead function: " + shortident + "'); abort(-1);");
+ delete LibraryManager.library[shortident + '__inline'];
+ delete LibraryManager.library[shortident + '__deps'];
+ }
+ }
if (!LINKABLE && !LibraryManager.library.hasOwnProperty(shortident) && !LibraryManager.library.hasOwnProperty(shortident + '__inline')) {
if (ERROR_ON_UNDEFINED_SYMBOLS) error('unresolved symbol: ' + shortident);
- if (VERBOSE || WARN_ON_UNDEFINED_SYMBOLS) printErr('warning: unresolved symbol: ' + shortident);
- if (ASM_JS || item.ident in DEAD_FUNCTIONS) {
+ else if (VERBOSE || WARN_ON_UNDEFINED_SYMBOLS) warn('unresolved symbol: ' + shortident);
+ if (ASM_JS) {
// emit a stub that will fail during runtime. this allows asm validation to succeed.
LibraryManager.library[shortident] = new Function("Module['printErr']('missing function: " + shortident + "'); abort(-1);");
} else {
@@ -756,14 +765,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 +835,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';
@@ -948,11 +946,12 @@ function JSify(data, functionsOnly, givenFunctions) {
}
if (item.valueType[item.valueType.length-1] === '>') {
// vector store TODO: move to makeSetValue?
- var base = getVectorBaseType(item.valueType);
- return '(' + makeSetValue(item.ident, 0, value + '.x', base, 0, 0, item.align) + ',' +
- makeSetValue(item.ident, 4, value + '.y', base, 0, 0, item.align) + ',' +
- makeSetValue(item.ident, 8, value + '.z', base, 0, 0, item.align) + ',' +
- makeSetValue(item.ident, 12, value + '.w', base, 0, 0, item.align) + ')';
+ var native = getVectorNativeType(item.valueType);
+ var base = getSIMDName(native);
+ return '(' + makeSetValue(item.ident, 0, value + '.x', native, 0, 0, item.align) + ',' +
+ makeSetValue(item.ident, 4, value + '.y', native, 0, 0, item.align) + ',' +
+ makeSetValue(item.ident, 8, value + '.z', native, 0, 0, item.align) + ',' +
+ makeSetValue(item.ident, 12, value + '.w', native, 0, 0, item.align) + ');';
}
switch (impl) {
case VAR_NATIVIZED:
@@ -1323,11 +1322,12 @@ function JSify(data, functionsOnly, givenFunctions) {
var value = finalizeLLVMParameter(item.pointer);
if (item.valueType[item.valueType.length-1] === '>') {
// vector load
- var base = getVectorBaseType(item.valueType);
- return base + '32x4(' + makeGetValue(value, 0, base, 0, item.unsigned, 0, item.align) + ',' +
- makeGetValue(value, 4, base, 0, item.unsigned, 0, item.align) + ',' +
- makeGetValue(value, 8, base, 0, item.unsigned, 0, item.align) + ',' +
- makeGetValue(value, 12, base, 0, item.unsigned, 0, item.align) + ')';
+ var native = getVectorNativeType(item.valueType);
+ var base = getSIMDName(native);
+ return base + '32x4(' + makeGetValue(value, 0, native, 0, item.unsigned, 0, item.align) + ',' +
+ makeGetValue(value, 4, native, 0, item.unsigned, 0, item.align) + ',' +
+ makeGetValue(value, 8, native, 0, item.unsigned, 0, item.align) + ',' +
+ makeGetValue(value, 12, native, 0, item.unsigned, 0, item.align) + ');';
}
var impl = item.ident ? getVarImpl(item.funcData, item.ident) : VAR_EMULATED;
switch (impl) {
@@ -1335,7 +1335,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 + '";';
@@ -1489,7 +1489,7 @@ function JSify(data, functionsOnly, givenFunctions) {
}
params.forEach(function(param, i) {
- var val = finalizeParam(param);
+ var val = finalizeLLVMParameter(param);
if (!hasVarArgs || useJSArgs || i < normalArgs) {
args.push(val);
argsTypes.push(param.type);
@@ -1512,8 +1512,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]) });
}
@@ -1590,7 +1592,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;
@@ -1626,7 +1628,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
}
@@ -1698,7 +1704,7 @@ function JSify(data, functionsOnly, givenFunctions) {
//
if (!mainPass) {
- if (phase == 'pre' && !Variables.generatedGlobalBase && !BUILD_AS_SHARED_LIB) {
+ if ((phase == 'pre' || phase == 'glue') && !Variables.generatedGlobalBase && !BUILD_AS_SHARED_LIB) {
Variables.generatedGlobalBase = true;
// Globals are done, here is the rest of static memory
assert((TARGET_LE32 && Runtime.GLOBAL_BASE == 8) || (TARGET_X86 && Runtime.GLOBAL_BASE == 4)); // this is assumed in e.g. relocations for linkable modules
@@ -1713,7 +1719,7 @@ function JSify(data, functionsOnly, givenFunctions) {
var generated = itemsDict.function.concat(itemsDict.type).concat(itemsDict.GlobalVariableStub).concat(itemsDict.GlobalVariable);
print(generated.map(function(item) { return item.JS; }).join('\n'));
- if (phase == 'pre') {
+ if (phase == 'pre' || phase == 'glue') {
if (memoryInitialization.length > 0) {
// apply postsets directly into the big memory initialization
itemsDict.GlobalVariablePostSet = itemsDict.GlobalVariablePostSet.filter(function(item) {
@@ -1736,7 +1742,7 @@ function JSify(data, functionsOnly, givenFunctions) {
});
// write out the singleton big memory initialization value
print('/* memory initializer */ ' + makePointer(memoryInitialization, null, 'ALLOC_NONE', 'i8', 'Runtime.GLOBAL_BASE' + (SIDE_MODULE ? '+H_BASE' : ''), true));
- } else {
+ } else if (phase !== 'glue') {
print('/* no memory initializer */'); // test purposes
}
@@ -1774,7 +1780,7 @@ function JSify(data, functionsOnly, givenFunctions) {
}
// Print out global variables and postsets TODO: batching
- if (phase == 'pre') {
+ if (phase == 'pre' || phase == 'glue') {
var legalizedI64sDefault = legalizedI64s;
legalizedI64s = false;
diff --git a/src/library.js b/src/library.js
index 875d8bab..8425a10f 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;
}
},
@@ -1193,7 +1190,6 @@ LibraryManager.library = {
_exit: function(status) {
// void _exit(int status);
// http://pubs.opengroup.org/onlinepubs/000095399/functions/exit.html
- Module.print('exit(' + status + ') called');
Module['exit'](status);
},
fork__deps: ['__setErrNo', '$ERRNO_CODES'],
@@ -1293,10 +1289,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;
}
},
@@ -1579,12 +1572,12 @@ LibraryManager.library = {
// stdio.h
// ==========================================================================
- _isFloat: function(text) {
- return !!(/^[+-]?[0-9]*\.?[0-9]+([eE][+-]?[0-9]+)?$/.exec(text));
+ _getFloat: function(text) {
+ return /^[+-]?[0-9]*\.?[0-9]+([eE][+-]?[0-9]+)?/.exec(text);
},
// TODO: Document.
- _scanString__deps: ['_isFloat'],
+ _scanString__deps: ['_getFloat'],
_scanString: function(format, get, unget, varargs) {
if (!__scanString.whiteSpace) {
__scanString.whiteSpace = {};
@@ -1602,12 +1595,12 @@ LibraryManager.library = {
if (format.indexOf('%n') >= 0) {
// need to track soFar
var _get = get;
- get = function() {
+ get = function get() {
soFar++;
return _get();
}
var _unget = unget;
- unget = function() {
+ unget = function unget() {
soFar--;
return _unget();
}
@@ -1743,15 +1736,13 @@ LibraryManager.library = {
// Read characters according to the format. floats are trickier, they may be in an unfloat state in the middle, then be a valid float later
if (type == 'f' || type == 'e' || type == 'g' ||
type == 'F' || type == 'E' || type == 'G') {
- var last = 0;
next = get();
- while (next > 0) {
+ while (next > 0 && (!(next in __scanString.whiteSpace))) {
buffer.push(String.fromCharCode(next));
- if (__isFloat(buffer.join(''))) {
- last = buffer.length;
- }
next = get();
}
+ var m = __getFloat(buffer.join(''));
+ var last = m ? m[0].length : 0;
for (var i = 0; i < buffer.length - last + 1; i++) {
unget();
}
@@ -1864,7 +1855,11 @@ LibraryManager.library = {
// int x = 4; printf("%c\n", (char)x);
var ret;
if (type === 'double') {
+#if TARGET_LE32 == 2
+ ret = {{{ makeGetValue('varargs', 'argIndex', 'double', undefined, undefined, true, 4) }}};
+#else
ret = {{{ makeGetValue('varargs', 'argIndex', 'double', undefined, undefined, true) }}};
+#endif
#if USE_TYPED_ARRAYS == 2
} else if (type == 'i64') {
@@ -1885,7 +1880,11 @@ LibraryManager.library = {
type = 'i32'; // varargs are always i32, i64, or double
ret = {{{ makeGetValue('varargs', 'argIndex', 'i32', undefined, undefined, true) }}};
}
+#if TARGET_LE32 == 2
+ argIndex += Runtime.getNativeFieldSize(type);
+#else
argIndex += Math.max(Runtime.getNativeFieldSize(type), Runtime.getAlignSize(type, null, true));
+#endif
return ret;
}
@@ -2701,10 +2700,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'],
@@ -2757,12 +2753,12 @@ LibraryManager.library = {
return -1;
}
var buffer = [];
- var get = function() {
+ function get() {
var c = _fgetc(stream);
buffer.push(c);
return c;
};
- var unget = function() {
+ function unget() {
_ungetc(buffer.pop(), stream);
};
return __scanString(format, get, unget, varargs);
@@ -2779,8 +2775,8 @@ LibraryManager.library = {
// int sscanf(const char *restrict s, const char *restrict format, ... );
// http://pubs.opengroup.org/onlinepubs/000095399/functions/scanf.html
var index = 0;
- var get = function() { return {{{ makeGetValue('s', 'index++', 'i8') }}}; };
- var unget = function() { index--; };
+ function get() { return {{{ makeGetValue('s', 'index++', 'i8') }}}; };
+ function unget() { index--; };
return __scanString(format, get, unget, varargs);
},
snprintf__deps: ['_formatString'],
@@ -3042,7 +3038,7 @@ LibraryManager.library = {
},
bsearch: function(key, base, num, size, compar) {
- var cmp = function(x, y) {
+ function cmp(x, y) {
#if ASM_JS
return Module['dynCall_iii'](compar, x, y);
#else
@@ -3205,7 +3201,7 @@ LibraryManager.library = {
}
}
if (!finalBase) finalBase = 10;
- start = str;
+ var start = str;
// Get digits.
var chr;
@@ -3345,10 +3341,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;
}
@@ -3978,10 +3971,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 {
@@ -4166,6 +4156,11 @@ LibraryManager.library = {
},
// ==========================================================================
+ // GCC/LLVM specifics
+ // ==========================================================================
+ __builtin_prefetch: function(){},
+
+ // ==========================================================================
// LLVM specifics
// ==========================================================================
@@ -5064,10 +5059,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;
@@ -5110,7 +5102,7 @@ LibraryManager.library = {
table[from + i] = {};
sigs.forEach(function(sig) { // TODO: new Function etc.
var full = 'dynCall_' + sig;
- table[from + i][sig] = function() {
+ table[from + i][sig] = function dynCall_sig() {
arguments[0] -= from;
return asm[full].apply(null, arguments);
}
@@ -5134,7 +5126,7 @@ LibraryManager.library = {
// patch js module dynCall_* to use functionTable
sigs.forEach(function(sig) {
- jsModule['dynCall_' + sig] = function() {
+ jsModule['dynCall_' + sig] = function dynCall_sig() {
return table[arguments[0]][sig].apply(null, arguments);
};
});
@@ -5297,6 +5289,16 @@ LibraryManager.library = {
}
},
+ dladdr: function(addr, info) {
+ // report all function pointers as coming from this program itself XXX not really correct in any way
+ var fname = allocate(intArrayFromString("/bin/this.program"), 'i8', ALLOC_NORMAL); // XXX leak
+ {{{ makeSetValue('addr', 0, 'fname', 'i32') }}};
+ {{{ makeSetValue('addr', QUANTUM_SIZE, '0', 'i32') }}};
+ {{{ makeSetValue('addr', QUANTUM_SIZE*2, '0', 'i32') }}};
+ {{{ makeSetValue('addr', QUANTUM_SIZE*3, '0', 'i32') }}};
+ return 1;
+ },
+
// ==========================================================================
// pwd.h
// ==========================================================================
@@ -5598,7 +5600,7 @@ LibraryManager.library = {
var WEEKDAYS = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
var MONTHS = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
- var leadingSomething = function(value, digits, character) {
+ function leadingSomething(value, digits, character) {
var str = typeof value === 'number' ? value.toString() : (value || '');
while (str.length < digits) {
str = character[0]+str;
@@ -5606,12 +5608,12 @@ LibraryManager.library = {
return str;
};
- var leadingNulls = function(value, digits) {
+ function leadingNulls(value, digits) {
return leadingSomething(value, digits, '0');
};
- var compareByDay = function(date1, date2) {
- var sgn = function(value) {
+ function compareByDay(date1, date2) {
+ function sgn(value) {
return value < 0 ? -1 : (value > 0 ? 1 : 0);
};
@@ -5624,7 +5626,7 @@ LibraryManager.library = {
return compare;
};
- var getFirstWeekStartDate = function(janFourth) {
+ function getFirstWeekStartDate(janFourth) {
switch (janFourth.getDay()) {
case 0: // Sunday
return new Date(janFourth.getFullYear()-1, 11, 29);
@@ -5643,7 +5645,7 @@ LibraryManager.library = {
}
};
- var getWeekBasedYear = function(date) {
+ function getWeekBasedYear(date) {
var thisDate = __addDays(new Date(date.tm_year+1900, 0, 1), date.tm_yday);
var janFourthThisYear = new Date(thisDate.getFullYear(), 0, 4);
@@ -5930,8 +5932,8 @@ LibraryManager.library = {
var matches = new RegExp('^'+pattern).exec(Pointer_stringify(buf))
// Module['print'](Pointer_stringify(buf)+ ' is matched by '+((new RegExp('^'+pattern)).source)+' into: '+JSON.stringify(matches));
- var initDate = function() {
- var fixup = function(value, min, max) {
+ function initDate() {
+ function fixup(value, min, max) {
return (typeof value !== 'number' || isNaN(value)) ? min : (value>=min ? (value<=max ? value: max): min);
};
return {
@@ -5948,7 +5950,7 @@ LibraryManager.library = {
var date = initDate();
var value;
- var getMatch = function(symbol) {
+ function getMatch(symbol) {
var pos = capture.indexOf(symbol);
// check if symbol appears in regexp
if (pos >= 0) {
@@ -6118,16 +6120,23 @@ 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.
+ clock_gettime__deps: ['emscripten_get_now'],
clock_gettime: function(clk_id, tp) {
// int clock_gettime(clockid_t clk_id, struct timespec *tp);
- var now = Date.now();
+ var now;
+ if (clk_id === {{{ cDefine('CLOCK_REALTIME') }}}) {
+ now = Date.now();
+ } else {
+ now = _emscripten_get_now();
+ }
{{{ makeSetValue('tp', C_STRUCTS.timespec.tv_sec, 'Math.floor(now/1000)', 'i32') }}}; // seconds
- {{{ makeSetValue('tp', C_STRUCTS.timespec.tv_nsec, '(now % 1000) * 1000 * 1000', 'i32') }}}; // nanoseconds (really milliseconds)
+ {{{ makeSetValue('tp', C_STRUCTS.timespec.tv_nsec, 'Math.floor((now % 1000)*1000*1000)', 'i32') }}}; // nanoseconds
return 0;
},
clock_settime: function(clk_id, tp) {
@@ -6135,10 +6144,17 @@ LibraryManager.library = {
// Nothing.
return 0;
},
+ clock_getres__deps: ['emscripten_get_now_res'],
clock_getres: function(clk_id, res) {
// int clock_getres(clockid_t clk_id, struct timespec *res);
+ var nsec;
+ if (clk_id === {{{ cDefine('CLOCK_REALTIME') }}}) {
+ nsec = 1000 * 1000;
+ } else {
+ nsec = _emscripten_get_now_res();
+ }
{{{ makeSetValue('res', C_STRUCTS.timespec.tv_sec, '1', 'i32') }}}
- {{{ makeSetValue('res', C_STRUCTS.timespec.tv_nsec, '1000 * 1000', 'i32') }}} // resolution is milliseconds
+ {{{ makeSetValue('res', C_STRUCTS.timespec.tv_nsec, 'nsec', 'i32') }}} // resolution is milliseconds
return 0;
},
@@ -6559,10 +6575,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;
},
@@ -6878,6 +6891,10 @@ LibraryManager.library = {
pthread_mutex_trylock: function() {
return 0;
},
+ pthread_mutexattr_setpshared: function(attr, pshared) {
+ // XXX implement if/when getpshared is required
+ return 0;
+ },
pthread_cond_init: function() {},
pthread_cond_destroy: function() {},
pthread_cond_broadcast: function() {
@@ -6973,6 +6990,10 @@ LibraryManager.library = {
_pthread_cleanup_push.level = __ATEXIT__.length;
},
+ pthread_rwlock_init: function() {
+ return 0; // XXX
+ },
+
// ==========================================================================
// malloc.h
// ==========================================================================
@@ -7314,6 +7335,7 @@ LibraryManager.library = {
// we're generating fake IP addresses with lookup_name that we can
// resolve later on with lookup_addr.
// We do the aliasing in 172.29.*.*, giving us 65536 possibilities.
+ $DNS__deps: ['_inet_pton4_raw', '_inet_pton6_raw'],
$DNS: {
address_map: {
id: 1,
@@ -7321,7 +7343,6 @@ LibraryManager.library = {
names: {}
},
- lookup_name__deps: ['_inet_pton4_raw', '_inet_pton6_raw'],
lookup_name: function (name) {
// If the name is already a valid ipv4 / ipv6 address, don't generate a fake one.
var res = __inet_pton4_raw(name);
@@ -7412,6 +7433,9 @@ LibraryManager.library = {
getaddrinfo__deps: ['$Sockets', '$DNS', '_inet_pton4_raw', '_inet_ntop4_raw', '_inet_pton6_raw', '_inet_ntop6_raw', '_write_sockaddr', 'htonl'],
getaddrinfo: function(node, service, hint, out) {
+ // Note getaddrinfo currently only returns a single addrinfo with ai_next defaulting to NULL. When NULL
+ // hints are specified or ai_family set to AF_UNSPEC or ai_socktype or ai_protocol set to 0 then we
+ // really should provide a linked list of suitable addrinfo values.
var addrs = [];
var canon = null;
var addr = 0;
@@ -7466,6 +7490,15 @@ LibraryManager.library = {
type = proto === {{{ cDefine('IPPROTO_UDP') }}} ? {{{ cDefine('SOCK_DGRAM') }}} : {{{ cDefine('SOCK_STREAM') }}};
}
+ // If type or proto are set to zero in hints we should really be returning multiple addrinfo values, but for
+ // now default to a TCP STREAM socket so we can at least return a sensible addrinfo given NULL hints.
+ if (proto === 0) {
+ proto = {{{ cDefine('IPPROTO_TCP') }}};
+ }
+ if (type === 0) {
+ type = {{{ cDefine('SOCK_STREAM') }}};
+ }
+
if (!node && !service) {
return {{{ cDefine('EAI_NONAME') }}};
}
@@ -7473,14 +7506,14 @@ LibraryManager.library = {
{{{ cDefine('AI_NUMERICSERV') }}}|{{{ cDefine('AI_V4MAPPED') }}}|{{{ cDefine('AI_ALL') }}}|{{{ cDefine('AI_ADDRCONFIG') }}})) {
return {{{ cDefine('EAI_BADFLAGS') }}};
}
- if (({{{ makeGetValue('hint', C_STRUCTS.addrinfo.ai_flags, 'i32') }}} & {{{ cDefine('AI_CANONNAME') }}}) && !node) {
+ if (hint !== 0 && ({{{ makeGetValue('hint', C_STRUCTS.addrinfo.ai_flags, 'i32') }}} & {{{ cDefine('AI_CANONNAME') }}}) && !node) {
return {{{ cDefine('EAI_BADFLAGS') }}};
}
if (flags & {{{ cDefine('AI_ADDRCONFIG') }}}) {
// TODO
return {{{ cDefine('EAI_NONAME') }}};
}
- if (type !== {{{ cDefine('SOCK_STREAM') }}} && type !== {{{ cDefine('SOCK_DGRAM') }}}) {
+ if (type !== 0 && type !== {{{ cDefine('SOCK_STREAM') }}} && type !== {{{ cDefine('SOCK_DGRAM') }}}) {
return {{{ cDefine('EAI_SOCKTYPE') }}};
}
if (family !== {{{ cDefine('AF_UNSPEC') }}} && family !== {{{ cDefine('AF_INET') }}} && family !== {{{ cDefine('AF_INET6') }}}) {
@@ -7610,12 +7643,43 @@ LibraryManager.library = {
return 0;
},
+ // Can't use a literal for $GAI_ERRNO_MESSAGES as was done for $ERRNO_MESSAGES as the keys (e.g. EAI_BADFLAGS)
+ // are actually negative numbers and you can't have expressions as keys in JavaScript literals.
+ $GAI_ERRNO_MESSAGES: {},
+ gai_strerror__deps: ['$GAI_ERRNO_MESSAGES'],
gai_strerror: function(val) {
- if (!_gai_strerror.error) {
- _gai_strerror.error = allocate(intArrayFromString("unknown error"), 'i8', ALLOC_NORMAL);
+ var buflen = 256;
+
+ // On first call to gai_strerror we initialise the buffer and populate the error messages.
+ if (!_gai_strerror.buffer) {
+ _gai_strerror.buffer = _malloc(buflen);
+
+ GAI_ERRNO_MESSAGES['0'] = 'Success';
+ GAI_ERRNO_MESSAGES['' + {{{ cDefine('EAI_BADFLAGS') }}}] = 'Invalid value for \'ai_flags\' field';
+ GAI_ERRNO_MESSAGES['' + {{{ cDefine('EAI_NONAME') }}}] = 'NAME or SERVICE is unknown';
+ GAI_ERRNO_MESSAGES['' + {{{ cDefine('EAI_AGAIN') }}}] = 'Temporary failure in name resolution';
+ GAI_ERRNO_MESSAGES['' + {{{ cDefine('EAI_FAIL') }}}] = 'Non-recoverable failure in name res';
+ GAI_ERRNO_MESSAGES['' + {{{ cDefine('EAI_FAMILY') }}}] = '\'ai_family\' not supported';
+ GAI_ERRNO_MESSAGES['' + {{{ cDefine('EAI_SOCKTYPE') }}}] = '\'ai_socktype\' not supported';
+ GAI_ERRNO_MESSAGES['' + {{{ cDefine('EAI_SERVICE') }}}] = 'SERVICE not supported for \'ai_socktype\'';
+ GAI_ERRNO_MESSAGES['' + {{{ cDefine('EAI_MEMORY') }}}] = 'Memory allocation failure';
+ GAI_ERRNO_MESSAGES['' + {{{ cDefine('EAI_SYSTEM') }}}] = 'System error returned in \'errno\'';
+ GAI_ERRNO_MESSAGES['' + {{{ cDefine('EAI_OVERFLOW') }}}] = 'Argument buffer overflow';
+ }
+
+ var msg = 'Unknown error';
+
+ if (val in GAI_ERRNO_MESSAGES) {
+ if (GAI_ERRNO_MESSAGES[val].length > buflen - 1) {
+ msg = 'Message too long'; // EMSGSIZE message. This should never occur given the GAI_ERRNO_MESSAGES above.
+ } else {
+ msg = GAI_ERRNO_MESSAGES[val];
+ }
}
- return _gai_strerror.error;
+
+ writeAsciiToMemory(msg, _gai_strerror.buffer);
+ return _gai_strerror.buffer;
},
// ==========================================================================
@@ -7683,7 +7747,7 @@ LibraryManager.library = {
var session = Module['webrtc']['session'];
var peer = new Peer(broker);
var listenOptions = Module['webrtc']['hostOptions'] || {};
- peer.onconnection = function(connection) {
+ peer.onconnection = function peer_onconnection(connection) {
console.log('connected');
var addr;
/* If this peer is connecting to the host, assign 10.0.0.1 to the host so it can be
@@ -7697,7 +7761,7 @@ LibraryManager.library = {
}
connection['addr'] = addr;
Sockets.connections[addr] = connection;
- connection.ondisconnect = function() {
+ connection.ondisconnect = function connection_ondisconnect() {
console.log('disconnect');
// Don't return the host address (10.0.0.1) to the pool
if (!(session && session === Sockets.connections[addr]['route'])) {
@@ -7709,12 +7773,12 @@ LibraryManager.library = {
Module['webrtc']['ondisconnect'](peer);
}
};
- connection.onerror = function(error) {
+ connection.onerror = function connection_onerror(error) {
if (Module['webrtc']['onerror'] && 'function' === typeof Module['webrtc']['onerror']) {
Module['webrtc']['onerror'](error);
}
};
- connection.onmessage = function(label, message) {
+ connection.onmessage = function connection_onmessage(label, message) {
if ('unreliable' === label) {
handleMessage(addr, message.data);
}
@@ -7724,13 +7788,13 @@ LibraryManager.library = {
Module['webrtc']['onconnect'](peer);
}
};
- peer.onpending = function(pending) {
+ peer.onpending = function peer_onpending(pending) {
console.log('pending from: ', pending['route'], '; initiated by: ', (pending['incoming']) ? 'remote' : 'local');
};
- peer.onerror = function(error) {
+ peer.onerror = function peer_onerror(error) {
console.error(error);
};
- peer.onroute = function(route) {
+ peer.onroute = function peer_onroute(route) {
if (Module['webrtc']['onpeer'] && 'function' === typeof Module['webrtc']['onpeer']) {
Module['webrtc']['onpeer'](peer, route);
}
@@ -7746,7 +7810,7 @@ LibraryManager.library = {
console.log("unable to deliver message: ", addr, header[1], message);
}
}
- window.onbeforeunload = function() {
+ window.onbeforeunload = function window_onbeforeunload() {
var ids = Object.keys(Sockets.connections);
ids.forEach(function(id) {
Sockets.connections[id].close();
@@ -7815,7 +7879,7 @@ LibraryManager.library = {
}
info.addr = Sockets.localAddr; // 10.0.0.254
info.host = __inet_ntop4_raw(info.addr);
- info.close = function() {
+ info.close = function info_close() {
Sockets.portmap[info.port] = undefined;
}
Sockets.portmap[info.port] = info;
@@ -8181,7 +8245,7 @@ LibraryManager.library = {
},
accept__deps: ['$FS', '$SOCKFS', '$DNS', '$ERRNO_CODES', '__setErrNo', '_write_sockaddr'],
- accept: function(fd, addrp, addrlen) {
+ accept: function(fd, addr, addrlen) {
var sock = SOCKFS.getSocket(fd);
if (!sock) {
___setErrNo(ERRNO_CODES.EBADF);
@@ -8189,7 +8253,7 @@ LibraryManager.library = {
}
try {
var newsock = sock.sock_ops.accept(sock);
- if (addrp) {
+ if (addr) {
var res = __write_sockaddr(addr, newsock.family, DNS.lookup_name(newsock.daddr), newsock.dport);
assert(!res.errno);
}
@@ -8547,7 +8611,13 @@ LibraryManager.library = {
return -1;
}
var arg = {{{ makeGetValue('varargs', '0', 'i32') }}};
- return FS.ioctl(stream, request, arg);
+
+ try {
+ return FS.ioctl(stream, request, arg);
+ } catch (e) {
+ FS.handleFSError(e);
+ return -1;
+ }
},
#endif
@@ -8580,7 +8650,7 @@ LibraryManager.library = {
},
emscripten_run_script_string: function(ptr) {
- var s = eval(Pointer_stringify(ptr));
+ var s = eval(Pointer_stringify(ptr)) + '';
var me = _emscripten_run_script_string;
if (!me.bufferSize || me.bufferSize < s.length+1) {
if (me.bufferSize) _free(me.buffer);
@@ -8614,12 +8684,56 @@ LibraryManager.library = {
},
emscripten_asm_const: function(code) {
- // code is a constant string on the heap, so we can cache these
- if (!Runtime.asmConstCache) Runtime.asmConstCache = {};
- var func = Runtime.asmConstCache[code];
- if (func) return func();
- func = Runtime.asmConstCache[code] = eval('(function(){ ' + Pointer_stringify(code) + ' })'); // new Function does not allow upvars in node
- return func();
+ Runtime.getAsmConst(code, 0)();
+ },
+
+ emscripten_asm_const_int__jsargs: true,
+ emscripten_asm_const_int: function(code) {
+ var args = Array.prototype.slice.call(arguments, 1);
+ return Runtime.getAsmConst(code, args.length).apply(null, args) | 0;
+ },
+
+ emscripten_asm_const_double__jsargs: true,
+ emscripten_asm_const_double: function(code) {
+ var args = Array.prototype.slice.call(arguments, 1);
+ return +Runtime.getAsmConst(code, args.length).apply(null, args);
+ },
+
+ emscripten_get_now: function() {
+ if (!_emscripten_get_now.actual) {
+ if (ENVIRONMENT_IS_NODE) {
+ _emscripten_get_now.actual = function _emscripten_get_now_actual() {
+ var t = process['hrtime']();
+ return t[0] * 1e3 + t[1] / 1e6;
+ }
+ } else if (typeof dateNow !== 'undefined') {
+ _emscripten_get_now.actual = dateNow;
+ } else if (ENVIRONMENT_IS_WEB && window['performance'] && window['performance']['now']) {
+ _emscripten_get_now.actual = function _emscripten_get_now_actual() { return window['performance']['now'](); };
+ } else {
+ _emscripten_get_now.actual = Date.now;
+ }
+ }
+ return _emscripten_get_now.actual();
+ },
+
+ emscripten_get_now_res: function() { // return resolution of get_now, in nanoseconds
+ if (ENVIRONMENT_IS_NODE) {
+ return 1; // nanoseconds
+ } else if (typeof dateNow !== 'undefined' ||
+ (ENVIRONMENT_IS_WEB && window['performance'] && window['performance']['now'])) {
+ return 1000; // microseconds (1/1000 of a millisecond)
+ } else {
+ return 1000*1000; // milliseconds
+ }
+ },
+
+ //============================
+ // emscripten vector ops
+ //============================
+
+ emscripten_float32x4_signmask__inline: function(x) {
+ return x + '.signMask()';
},
//============================
@@ -8718,6 +8832,6 @@ function autoAddDeps(object, name) {
// Add aborting stubs for various libc stuff needed by libc++
['pthread_cond_signal', 'pthread_equal', 'wcstol', 'wcstoll', 'wcstoul', 'wcstoull', 'wcstof', 'wcstod', 'wcstold', 'pthread_join', 'pthread_detach', 'catgets', 'catopen', 'catclose', 'fputwc', '__lockfile', '__unlockfile'].forEach(function(aborter) {
- LibraryManager.library[aborter] = function() { throw 'TODO: ' + aborter };
+ LibraryManager.library[aborter] = function aborting_stub() { throw 'TODO: ' + aborter };
});
diff --git a/src/library_browser.js b/src/library_browser.js
index 59d2945e..8444fb73 100644
--- a/src/library_browser.js
+++ b/src/library_browser.js
@@ -4,12 +4,12 @@
mergeInto(LibraryManager.library, {
$Browser__deps: ['$PATH'],
- $Browser__postset: 'Module["requestFullScreen"] = function(lockPointer, resizeCanvas) { Browser.requestFullScreen(lockPointer, resizeCanvas) };\n' + // exports
- 'Module["requestAnimationFrame"] = function(func) { Browser.requestAnimationFrame(func) };\n' +
- 'Module["setCanvasSize"] = function(width, height, noUpdates) { Browser.setCanvasSize(width, height, noUpdates) };\n' +
- 'Module["pauseMainLoop"] = function() { Browser.mainLoop.pause() };\n' +
- 'Module["resumeMainLoop"] = function() { Browser.mainLoop.resume() };\n' +
- 'Module["getUserMedia"] = function() { Browser.getUserMedia() }',
+ $Browser__postset: 'Module["requestFullScreen"] = function Module_requestFullScreen(lockPointer, resizeCanvas) { Browser.requestFullScreen(lockPointer, resizeCanvas) };\n' + // exports
+ 'Module["requestAnimationFrame"] = function Module_requestAnimationFrame(func) { Browser.requestAnimationFrame(func) };\n' +
+ 'Module["setCanvasSize"] = function Module_setCanvasSize(width, height, noUpdates) { Browser.setCanvasSize(width, height, noUpdates) };\n' +
+ 'Module["pauseMainLoop"] = function Module_pauseMainLoop() { Browser.mainLoop.pause() };\n' +
+ 'Module["resumeMainLoop"] = function Module_resumeMainLoop() { Browser.mainLoop.resume() };\n' +
+ 'Module["getUserMedia"] = function Module_getUserMedia() { Browser.getUserMedia() }',
$Browser: {
mainLoop: {
scheduler: null,
@@ -77,10 +77,10 @@ mergeInto(LibraryManager.library, {
// might create some side data structure for use later (like an Image element, etc.).
var imagePlugin = {};
- imagePlugin['canHandle'] = function(name) {
+ imagePlugin['canHandle'] = function imagePlugin_canHandle(name) {
return !Module.noImageDecoding && /\.(jpg|jpeg|png|bmp)$/i.test(name);
};
- imagePlugin['handle'] = function(byteArray, name, onload, onerror) {
+ imagePlugin['handle'] = function imagePlugin_handle(byteArray, name, onload, onerror) {
var b = null;
if (Browser.hasBlobConstructor) {
try {
@@ -103,7 +103,7 @@ mergeInto(LibraryManager.library, {
assert(typeof url == 'string', 'createObjectURL must return a url as a string');
#endif
var img = new Image();
- img.onload = function() {
+ img.onload = function img_onload() {
assert(img.complete, 'Image ' + name + ' could not be decoded');
var canvas = document.createElement('canvas');
canvas.width = img.width;
@@ -114,7 +114,7 @@ mergeInto(LibraryManager.library, {
Browser.URLObject.revokeObjectURL(url);
if (onload) onload(byteArray);
};
- img.onerror = function(event) {
+ img.onerror = function img_onerror(event) {
console.log('Image ' + url + ' could not be decoded');
if (onerror) onerror();
};
@@ -123,10 +123,10 @@ mergeInto(LibraryManager.library, {
Module['preloadPlugins'].push(imagePlugin);
var audioPlugin = {};
- audioPlugin['canHandle'] = function(name) {
+ audioPlugin['canHandle'] = function audioPlugin_canHandle(name) {
return !Module.noAudioDecoding && name.substr(-4) in { '.ogg': 1, '.wav': 1, '.mp3': 1 };
};
- audioPlugin['handle'] = function(byteArray, name, onload, onerror) {
+ audioPlugin['handle'] = function audioPlugin_handle(byteArray, name, onload, onerror) {
var done = false;
function finish(audio) {
if (done) return;
@@ -152,7 +152,7 @@ mergeInto(LibraryManager.library, {
#endif
var audio = new Audio();
audio.addEventListener('canplaythrough', function() { finish(audio) }, false); // use addEventListener due to chromium bug 124926
- audio.onerror = function(event) {
+ audio.onerror = function audio_onerror(event) {
if (done) return;
console.log('warning: browser could not fully decode audio ' + name + ', trying slower base64 approach');
function encode64(data) {
@@ -250,7 +250,9 @@ mergeInto(LibraryManager.library, {
contextAttributes.preserveDrawingBuffer = true;
#endif
- ctx = canvas.getContext('experimental-webgl', contextAttributes);
+ ['experimental-webgl', 'webgl'].some(function(webglId) {
+ return ctx = canvas.getContext(webglId, contextAttributes);
+ });
} else {
ctx = canvas.getContext('2d');
}
@@ -268,7 +270,7 @@ mergeInto(LibraryManager.library, {
(function(prop) {
switch (typeof tempCtx[prop]) {
case 'function': {
- wrapper[prop] = function() {
+ wrapper[prop] = function gl_wrapper() {
if (GL.debug) {
var printArgs = Array.prototype.slice.call(arguments).map(Runtime.prettyPrint);
Module.printErr('[gl_f:' + prop + ':' + printArgs + ']');
@@ -359,16 +361,20 @@ mergeInto(LibraryManager.library, {
canvas.requestFullScreen();
},
- requestAnimationFrame: function(func) {
- if (!window.requestAnimationFrame) {
- window.requestAnimationFrame = window['requestAnimationFrame'] ||
- window['mozRequestAnimationFrame'] ||
- window['webkitRequestAnimationFrame'] ||
- window['msRequestAnimationFrame'] ||
- window['oRequestAnimationFrame'] ||
- window['setTimeout'];
+ requestAnimationFrame: function requestAnimationFrame(func) {
+ if (typeof window === 'undefined') { // Provide fallback to setTimeout if window is undefined (e.g. in Node.js)
+ setTimeout(func, 1000/60);
+ } else {
+ if (!window.requestAnimationFrame) {
+ window.requestAnimationFrame = window['requestAnimationFrame'] ||
+ window['mozRequestAnimationFrame'] ||
+ window['webkitRequestAnimationFrame'] ||
+ window['msRequestAnimationFrame'] ||
+ window['oRequestAnimationFrame'] ||
+ window['setTimeout'];
+ }
+ window.requestAnimationFrame(func);
}
- window.requestAnimationFrame(func);
},
// generic abort-aware wrapper for an async callback
@@ -497,7 +503,7 @@ mergeInto(LibraryManager.library, {
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.responseType = 'arraybuffer';
- xhr.onload = function() {
+ xhr.onload = function xhr_onload() {
if (xhr.status == 200 || (xhr.status == 0 && xhr.response)) { // file URLs can return 0
onload(xhr.response);
} else {
@@ -610,7 +616,7 @@ mergeInto(LibraryManager.library, {
http.responseType = 'arraybuffer';
// LOAD
- http.onload = function(e) {
+ http.onload = function http_onload(e) {
if (http.status == 200) {
FS.createDataFile( _file.substr(0, index), _file.substr(index + 1), new Uint8Array(http.response), true, true);
if (onload) Runtime.dynCall('vii', onload, [arg, file]);
@@ -620,12 +626,12 @@ mergeInto(LibraryManager.library, {
};
// ERROR
- http.onerror = function(e) {
+ http.onerror = function http_onerror(e) {
if (onerror) Runtime.dynCall('vii', onerror, [arg, http.status]);
};
// PROGRESS
- http.onprogress = function(e) {
+ http.onprogress = function http_onprogress(e) {
var percentComplete = (e.position / e.totalSize)*100;
if (onprogress) Runtime.dynCall('vii', onprogress, [arg, percentComplete]);
};
@@ -705,7 +711,7 @@ mergeInto(LibraryManager.library, {
assert(runDependencies === 0, 'async_load_script must be run when no other dependencies are active');
var script = document.createElement('script');
- script.onload = function() {
+ script.onload = function script_onload() {
if (runDependencies > 0) {
dependenciesFulfilled = onload;
} else {
@@ -720,7 +726,7 @@ mergeInto(LibraryManager.library, {
emscripten_set_main_loop: function(func, fps, simulateInfiniteLoop) {
Module['noExitRuntime'] = true;
- Browser.mainLoop.runner = function() {
+ Browser.mainLoop.runner = function Browser_mainLoop_runner() {
if (ABORT) return;
if (Browser.mainLoop.queue.length > 0) {
var start = Date.now();
@@ -777,11 +783,11 @@ mergeInto(LibraryManager.library, {
Browser.mainLoop.scheduler();
}
if (fps && fps > 0) {
- Browser.mainLoop.scheduler = function() {
+ Browser.mainLoop.scheduler = function Browser_mainLoop_scheduler() {
setTimeout(Browser.mainLoop.runner, 1000/fps); // doing this each time means that on exception, we stop
}
} else {
- Browser.mainLoop.scheduler = function() {
+ Browser.mainLoop.scheduler = function Browser_mainLoop_scheduler() {
Browser.requestAnimationFrame(Browser.mainLoop.runner);
}
}
@@ -867,24 +873,6 @@ mergeInto(LibraryManager.library, {
{{{ makeSetValue('isFullscreen', '0', 'Browser.isFullScreen ? 1 : 0', 'i32') }}};
},
- emscripten_get_now: function() {
- if (!_emscripten_get_now.actual) {
- if (ENVIRONMENT_IS_NODE) {
- _emscripten_get_now.actual = function() {
- var t = process['hrtime']();
- return t[0] * 1e3 + t[1] / 1e6;
- }
- } else if (typeof dateNow !== 'undefined') {
- _emscripten_get_now.actual = dateNow;
- } else if (ENVIRONMENT_IS_WEB && window['performance'] && window['performance']['now']) {
- _emscripten_get_now.actual = function() { return window['performance']['now'](); };
- } else {
- _emscripten_get_now.actual = Date.now;
- }
- }
- return _emscripten_get_now.actual();
- },
-
emscripten_create_worker: function(url) {
url = Pointer_stringify(url);
var id = Browser.workers.length;
@@ -895,7 +883,7 @@ mergeInto(LibraryManager.library, {
buffer: 0,
bufferSize: 0
};
- info.worker.onmessage = function(msg) {
+ info.worker.onmessage = function info_worker_onmessage(msg) {
var info = Browser.workers[id];
if (!info) return; // worker was destroyed meanwhile
var callbackId = msg.data['callbackId'];
diff --git a/src/library_egl.js b/src/library_egl.js
index c25dc8ef..73d5e544 100644
--- a/src/library_egl.js
+++ b/src/library_egl.js
@@ -9,10 +9,16 @@
var LibraryEGL = {
$EGL: {
// This variable tracks the success status of the most recently invoked EGL function call.
- eglErrorCode: 0x3000 /* EGL_SUCCESS */,
+ errorCode: 0x3000 /* EGL_SUCCESS */,
+ defaultDisplayInitialized: false,
+ currentContext: 0 /* EGL_NO_CONTEXT */,
+ currentReadSurface: 0 /* EGL_NO_SURFACE */,
+ currentDrawSurface: 0 /* EGL_NO_SURFACE */,
+
+ stringCache: {},
setErrorCode: function(code) {
- EGL.eglErrorCode = code;
+ EGL.errorCode = code;
},
chooseConfig: function(display, attribList, config, config_size, numConfigs) {
@@ -63,6 +69,7 @@ var LibraryEGL = {
if (minorVersion) {
{{{ makeSetValue('minorVersion', '0', '4', 'i32') }}}; // Advertise EGL Minor version: '4'
}
+ EGL.defaultDisplayInitialized = true;
EGL.setErrorCode(0x3000 /* EGL_SUCCESS */);
return 1;
}
@@ -78,18 +85,10 @@ var LibraryEGL = {
EGL.setErrorCode(0x3008 /* EGL_BAD_DISPLAY */);
return 0;
}
- // TODO: Tear down EGL here. Currently a no-op since we don't need to actually do anything here for the browser.
- EGL.setErrorCode(0x3000 /* EGL_SUCCESS */);
- return 1;
- },
-
-// EGLAPI EGLBoolean EGLAPIENTRY eglTerminate(EGLDisplay dpy);
- eglTerminate: function(display) {
- if (display != 62000 /* Magic ID for Emscripten 'default display' */) {
- EGL.setErrorCode(0x3008 /* EGL_BAD_DISPLAY */);
- return 0;
- }
- // TODO: Tear down EGL here. Currently a no-op since we don't need to actually do anything here for the browser.
+ EGL.currentContext = 0;
+ EGL.currentReadSurface = 0;
+ EGL.currentDrawSurface = 0;
+ EGL.defaultDisplayInitialized = false;
EGL.setErrorCode(0x3000 /* EGL_SUCCESS */);
return 1;
},
@@ -246,6 +245,12 @@ var LibraryEGL = {
EGL.setErrorCode(0x300D /* EGL_BAD_SURFACE */);
return 1;
}
+ if (EGL.currentReadSurface == surface) {
+ EGL.currentReadSurface = 0;
+ }
+ if (EGL.currentDrawSurface == surface) {
+ EGL.currentDrawSurface = 0;
+ }
EGL.setErrorCode(0x3000 /* EGL_SUCCESS */);
return 1; /* Magic ID for Emscripten 'default surface' */
},
@@ -263,6 +268,7 @@ var LibraryEGL = {
EGL.windowID = _glutCreateWindow();
if (EGL.windowID != 0) {
EGL.setErrorCode(0x3000 /* EGL_SUCCESS */);
+ // Note: This function only creates a context, but it shall not make it active.
return 62004; // Magic ID for Emscripten EGLContext
} else {
EGL.setErrorCode(0x3009 /* EGL_BAD_MATCH */); // By the EGL 1.4 spec, an implementation that does not support GLES2 (WebGL in this case), this error code is set.
@@ -278,10 +284,17 @@ var LibraryEGL = {
EGL.setErrorCode(0x3008 /* EGL_BAD_DISPLAY */);
return 0;
}
+ if (context != 62004 /* Magic ID for Emscripten EGLContext */) {
+ EGL.setErrorCode(0x3006 /* EGL_BAD_CONTEXT */);
+ return 0;
+ }
_glutDestroyWindow(EGL.windowID);
EGL.setErrorCode(0x3000 /* EGL_SUCCESS */);
- return 62004; // Magic ID for Emscripten EGLContext
+ if (EGL.currentContext == context) {
+ EGL.currentContext = 0;
+ }
+ return 1 /* EGL_TRUE */;
},
// EGLAPI EGLBoolean EGLAPIENTRY eglDestroyContext(EGLDisplay dpy, EGLContext ctx);
@@ -405,7 +418,7 @@ var LibraryEGL = {
// EGLAPI EGLint EGLAPIENTRY eglGetError(void);
eglGetError: function() {
- return EGL.eglErrorCode;
+ return EGL.errorCode;
},
// EGLAPI const char * EGLAPIENTRY eglQueryString(EGLDisplay dpy, EGLint name);
@@ -416,15 +429,19 @@ var LibraryEGL = {
}
//\todo An EGL_NOT_INITIALIZED error is generated if EGL is not initialized for dpy.
EGL.setErrorCode(0x3000 /* EGL_SUCCESS */);
+ if (EGL.stringCache[name]) return EGL.stringCache[name];
+ var ret;
switch(name) {
- case 0x3053 /* EGL_VENDOR */: return allocate(intArrayFromString("Emscripten"), 'i8', ALLOC_NORMAL);
- case 0x3054 /* EGL_VERSION */: return allocate(intArrayFromString("1.4 Emscripten EGL"), 'i8', ALLOC_NORMAL);
- case 0x3055 /* EGL_EXTENSIONS */: return allocate(intArrayFromString(""), 'i8', ALLOC_NORMAL); // Currently not supporting any EGL extensions.
- case 0x308D /* EGL_CLIENT_APIS */: return allocate(intArrayFromString("OpenGL_ES"), 'i8', ALLOC_NORMAL);
+ case 0x3053 /* EGL_VENDOR */: ret = allocate(intArrayFromString("Emscripten"), 'i8', ALLOC_NORMAL); break;
+ case 0x3054 /* EGL_VERSION */: ret = allocate(intArrayFromString("1.4 Emscripten EGL"), 'i8', ALLOC_NORMAL); break;
+ case 0x3055 /* EGL_EXTENSIONS */: ret = allocate(intArrayFromString(""), 'i8', ALLOC_NORMAL); break; // Currently not supporting any EGL extensions.
+ case 0x308D /* EGL_CLIENT_APIS */: ret = allocate(intArrayFromString("OpenGL_ES"), 'i8', ALLOC_NORMAL); break;
default:
EGL.setErrorCode(0x300C /* EGL_BAD_PARAMETER */);
return 0;
}
+ EGL.stringCache[name] = ret;
+ return ret;
},
// EGLAPI EGLBoolean EGLAPIENTRY eglBindAPI(EGLenum api);
@@ -471,21 +488,46 @@ var LibraryEGL = {
eglMakeCurrent: function(display, draw, read, context) {
if (display != 62000 /* Magic ID for Emscripten 'default display' */) {
EGL.setErrorCode(0x3008 /* EGL_BAD_DISPLAY */);
- return 0;
+ return 0 /* EGL_FALSE */;
}
//\todo An EGL_NOT_INITIALIZED error is generated if EGL is not initialized for dpy.
- if (context != 62004 /* Magic ID for Emscripten EGLContext */) {
+ if (context != 0 && context != 62004 /* Magic ID for Emscripten EGLContext */) {
EGL.setErrorCode(0x3006 /* EGL_BAD_CONTEXT */);
return 0;
}
- if (read != 62006 || draw != 62006 /* Magic ID for Emscripten 'default surface' */) {
+ if ((read != 0 && read != 62006) || (draw != 0 && draw != 62006 /* Magic ID for Emscripten 'default surface' */)) {
EGL.setErrorCode(0x300D /* EGL_BAD_SURFACE */);
return 0;
}
+ EGL.currentContext = context;
+ EGL.currentDrawSurface = draw;
+ EGL.currentReadSurface = read;
EGL.setErrorCode(0x3000 /* EGL_SUCCESS */);
- return 1;
+ return 1 /* EGL_TRUE */;
+ },
+
+ // EGLAPI EGLContext EGLAPIENTRY eglGetCurrentContext(void);
+ eglGetCurrentContext: function() {
+ return EGL.currentContext;
},
+ // EGLAPI EGLSurface EGLAPIENTRY eglGetCurrentSurface(EGLint readdraw);
+ eglGetCurrentSurface: function(readdraw) {
+ if (readdraw == 0x305A /* EGL_READ */) {
+ return EGL.currentReadSurface;
+ } else if (readdraw == 0x3059 /* EGL_DRAW */) {
+ return EGL.currentDrawSurface;
+ } else {
+ EGL.setErrorCode(0x300C /* EGL_BAD_PARAMETER */);
+ return 0 /* EGL_NO_SURFACE */;
+ }
+ },
+
+ // EGLAPI EGLDisplay EGLAPIENTRY eglGetCurrentDisplay(void);
+ eglGetCurrentDisplay: function() {
+ return EGL.currentContext ? 62000 /* Magic ID for Emscripten 'default display' */ : 0;
+ },
+
// EGLAPI EGLBoolean EGLAPIENTRY eglSwapBuffers(EGLDisplay dpy, EGLSurface surface);
eglSwapBuffers: function() {
EGL.setErrorCode(0x3000 /* EGL_SUCCESS */);
diff --git a/src/library_fs.js b/src/library_fs.js
index bd1522a8..5412185f 100644
--- a/src/library_fs.js
+++ b/src/library_fs.js
@@ -28,6 +28,7 @@ mergeInto(LibraryManager.library, {
ignorePermissions: true,
ErrnoError: null, // set during init
+ genericErrors: {},
handleFSError: function(e) {
if (!(e instanceof FS.ErrnoError)) throw e + ' : ' + stackTrace();
@@ -62,7 +63,7 @@ mergeInto(LibraryManager.library, {
}
current = FS.lookupNode(current, parts[i]);
- current_path = PATH.join(current_path, parts[i]);
+ current_path = PATH.join2(current_path, parts[i]);
// jump to the mount's root node if this is a mountpoint
if (FS.isMountpoint(current)) {
@@ -94,9 +95,11 @@ mergeInto(LibraryManager.library, {
var path;
while (true) {
if (FS.isRoot(node)) {
- return path ? PATH.join(node.mount.mountpoint, path) : node.mount.mountpoint;
+ var mount = node.mount.mountpoint;
+ if (!path) return mount;
+ return mount[mount.length-1] !== '/' ? mount + '/' + path : mount + path;
}
- path = path ? PATH.join(node.name, path) : node.name;
+ path = path ? node.name + '/' + path : node.name;
node = node.parent;
}
},
@@ -158,44 +161,50 @@ mergeInto(LibraryManager.library, {
return FS.lookup(parent, name);
},
createNode: function(parent, name, mode, rdev) {
- var node = {
- id: FS.nextInode++,
- name: name,
- mode: mode,
- node_ops: {},
- stream_ops: {},
- rdev: rdev,
- parent: null,
- mount: null
- };
- if (!parent) {
- parent = node; // root node sets parent to itself
- }
- node.parent = parent;
- node.mount = parent.mount;
- // compatibility
- var readMode = {{{ cDefine('S_IRUGO') }}} | {{{ cDefine('S_IXUGO') }}};
- var writeMode = {{{ cDefine('S_IWUGO') }}};
- // NOTE we must use Object.defineProperties instead of individual calls to
- // Object.defineProperty in order to make closure compiler happy
- Object.defineProperties(node, {
- read: {
- get: function() { return (node.mode & readMode) === readMode; },
- set: function(val) { val ? node.mode |= readMode : node.mode &= ~readMode; }
- },
- write: {
- get: function() { return (node.mode & writeMode) === writeMode; },
- set: function(val) { val ? node.mode |= writeMode : node.mode &= ~writeMode; }
- },
- isFolder: {
- get: function() { return FS.isDir(node.mode); },
- },
- isDevice: {
- get: function() { return FS.isChrdev(node.mode); },
- },
- });
- FS.hashAddNode(node);
- return node;
+ if (!FS.FSNode) {
+ FS.FSNode = function(parent, name, mode, rdev) {
+ this.id = FS.nextInode++;
+ this.name = name;
+ this.mode = mode;
+ this.node_ops = {};
+ this.stream_ops = {};
+ this.rdev = rdev;
+ this.parent = null;
+ this.mount = null;
+ if (!parent) {
+ parent = this; // root node sets parent to itself
+ }
+ this.parent = parent;
+ this.mount = parent.mount;
+ FS.hashAddNode(this);
+ };
+
+ // compatibility
+ var readMode = {{{ cDefine('S_IRUGO') }}} | {{{ cDefine('S_IXUGO') }}};
+ var writeMode = {{{ cDefine('S_IWUGO') }}};
+
+ FS.FSNode.prototype = {};
+
+ // NOTE we must use Object.defineProperties instead of individual calls to
+ // Object.defineProperty in order to make closure compiler happy
+ Object.defineProperties(FS.FSNode.prototype, {
+ read: {
+ get: function() { return (this.mode & readMode) === readMode; },
+ set: function(val) { val ? this.mode |= readMode : this.mode &= ~readMode; }
+ },
+ write: {
+ get: function() { return (this.mode & writeMode) === writeMode; },
+ set: function(val) { val ? this.mode |= writeMode : this.mode &= ~writeMode; }
+ },
+ isFolder: {
+ get: function() { return FS.isDir(this.mode); },
+ },
+ isDevice: {
+ get: function() { return FS.isChrdev(this.mode); },
+ },
+ });
+ }
+ return new FS.FSNode(parent, name, mode, rdev);
},
destroyNode: function(node) {
FS.hashRemoveNode(node);
@@ -351,24 +360,38 @@ mergeInto(LibraryManager.library, {
// object isn't directly passed in. not possible until
// SOCKFS is completed.
createStream: function(stream, fd_start, fd_end) {
+ if (!FS.FSStream) {
+ FS.FSStream = function(){};
+ FS.FSStream.prototype = {};
+ // compatibility
+ Object.defineProperties(FS.FSStream.prototype, {
+ object: {
+ get: function() { return this.node; },
+ set: function(val) { this.node = val; }
+ },
+ isRead: {
+ get: function() { return (this.flags & {{{ cDefine('O_ACCMODE') }}}) !== {{{ cDefine('O_WRONLY') }}}; }
+ },
+ isWrite: {
+ get: function() { return (this.flags & {{{ cDefine('O_ACCMODE') }}}) !== {{{ cDefine('O_RDONLY') }}}; }
+ },
+ isAppend: {
+ get: function() { return (this.flags & {{{ cDefine('O_APPEND') }}}); }
+ }
+ });
+ }
+ if (stream.__proto__) {
+ // reuse the object
+ stream.__proto__ = FS.FSStream.prototype;
+ } else {
+ var newStream = new FS.FSStream();
+ for (var p in stream) {
+ newStream[p] = stream[p];
+ }
+ stream = newStream;
+ }
var fd = FS.nextfd(fd_start, fd_end);
stream.fd = fd;
- // compatibility
- Object.defineProperties(stream, {
- object: {
- get: function() { return stream.node; },
- set: function(val) { stream.node = val; }
- },
- isRead: {
- get: function() { return (stream.flags & {{{ cDefine('O_ACCMODE') }}}) !== {{{ cDefine('O_WRONLY') }}}; }
- },
- isWrite: {
- get: function() { return (stream.flags & {{{ cDefine('O_ACCMODE') }}}) !== {{{ cDefine('O_RDONLY') }}}; }
- },
- isAppend: {
- get: function() { return (stream.flags & {{{ cDefine('O_APPEND') }}}); }
- }
- });
FS.streams[fd] = stream;
return stream;
},
@@ -426,7 +449,7 @@ mergeInto(LibraryManager.library, {
var completed = 0;
var total = FS.mounts.length;
- var done = function(err) {
+ function done(err) {
if (err) {
return callback(err);
}
@@ -771,7 +794,6 @@ mergeInto(LibraryManager.library, {
});
},
open: function(path, flags, mode, fd_start, fd_end) {
- path = PATH.normalize(path);
flags = typeof flags === 'string' ? FS.modeStringToFlags(flags) : flags;
mode = typeof mode === 'undefined' ? 0666 : mode;
if ((flags & {{{ cDefine('O_CREAT') }}})) {
@@ -780,13 +802,18 @@ mergeInto(LibraryManager.library, {
mode = 0;
}
var node;
- try {
- var lookup = FS.lookupPath(path, {
- follow: !(flags & {{{ cDefine('O_NOFOLLOW') }}})
- });
- node = lookup.node;
- } catch (e) {
- // ignore
+ if (typeof path === 'object') {
+ node = path;
+ } else {
+ path = PATH.normalize(path);
+ try {
+ var lookup = FS.lookupPath(path, {
+ follow: !(flags & {{{ cDefine('O_NOFOLLOW') }}})
+ });
+ node = lookup.node;
+ } catch (e) {
+ // ignore
+ }
}
// perhaps we need to create the node
if ((flags & {{{ cDefine('O_CREAT') }}})) {
@@ -1079,6 +1106,11 @@ mergeInto(LibraryManager.library, {
};
FS.ErrnoError.prototype = new Error();
FS.ErrnoError.prototype.constructor = FS.ErrnoError;
+ // Some errors may happen quite a bit, to avoid overhead we reuse them (and suffer a lack of stack info)
+ [ERRNO_CODES.ENOENT].forEach(function(code) {
+ FS.genericErrors[code] = new FS.ErrnoError(code);
+ FS.genericErrors[code].stack = '<generic error, no stack>';
+ });
},
staticInit: function() {
FS.ensureErrnoError();
@@ -1173,7 +1205,7 @@ mergeInto(LibraryManager.library, {
return ret;
},
createFolder: function(parent, name, canRead, canWrite) {
- var path = PATH.join(typeof parent === 'string' ? parent : FS.getPath(parent), name);
+ var path = PATH.join2(typeof parent === 'string' ? parent : FS.getPath(parent), name);
var mode = FS.getMode(canRead, canWrite);
return FS.mkdir(path, mode);
},
@@ -1183,7 +1215,7 @@ mergeInto(LibraryManager.library, {
while (parts.length) {
var part = parts.pop();
if (!part) continue;
- var current = PATH.join(parent, part);
+ var current = PATH.join2(parent, part);
try {
FS.mkdir(current);
} catch (e) {
@@ -1194,12 +1226,12 @@ mergeInto(LibraryManager.library, {
return current;
},
createFile: function(parent, name, properties, canRead, canWrite) {
- var path = PATH.join(typeof parent === 'string' ? parent : FS.getPath(parent), name);
+ var path = PATH.join2(typeof parent === 'string' ? parent : FS.getPath(parent), name);
var mode = FS.getMode(canRead, canWrite);
return FS.create(path, mode);
},
createDataFile: function(parent, name, data, canRead, canWrite, canOwn) {
- var path = name ? PATH.join(typeof parent === 'string' ? parent : FS.getPath(parent), name) : parent;
+ var path = name ? PATH.join2(typeof parent === 'string' ? parent : FS.getPath(parent), name) : parent;
var mode = FS.getMode(canRead, canWrite);
var node = FS.create(path, mode);
if (data) {
@@ -1209,16 +1241,16 @@ mergeInto(LibraryManager.library, {
data = arr;
}
// make sure we can write to the file
- FS.chmod(path, mode | {{{ cDefine('S_IWUGO') }}});
- var stream = FS.open(path, 'w');
+ FS.chmod(node, mode | {{{ cDefine('S_IWUGO') }}});
+ var stream = FS.open(node, 'w');
FS.write(stream, data, 0, data.length, 0, canOwn);
FS.close(stream);
- FS.chmod(path, mode);
+ FS.chmod(node, mode);
}
return node;
},
createDevice: function(parent, name, input, output) {
- var path = PATH.join(typeof parent === 'string' ? parent : FS.getPath(parent), name);
+ var path = PATH.join2(typeof parent === 'string' ? parent : FS.getPath(parent), name);
var mode = FS.getMode(!!input, !!output);
if (!FS.createDevice.major) FS.createDevice.major = 64;
var dev = FS.makedev(FS.createDevice.major++, 0);
@@ -1272,7 +1304,7 @@ mergeInto(LibraryManager.library, {
return FS.mkdev(path, mode, dev);
},
createLink: function(parent, name, target, canRead, canWrite) {
- var path = PATH.join(typeof parent === 'string' ? parent : FS.getPath(parent), name);
+ var path = PATH.join2(typeof parent === 'string' ? parent : FS.getPath(parent), name);
return FS.symlink(target, path);
},
// Makes sure a file's contents are loaded. Returns whether the file has
@@ -1304,11 +1336,11 @@ mergeInto(LibraryManager.library, {
if (typeof XMLHttpRequest !== 'undefined') {
if (!ENVIRONMENT_IS_WORKER) throw 'Cannot do synchronous binary XHRs outside webworkers in modern browsers. Use --embed-file or --preload-file in emcc';
// Lazy chunked Uint8Array (implements get and length from Uint8Array). Actual getting is abstracted away for eventual reuse.
- var LazyUint8Array = function() {
+ function LazyUint8Array() {
this.lengthKnown = false;
this.chunks = []; // Loaded chunks. Index is the chunk number
}
- LazyUint8Array.prototype.get = function(idx) {
+ LazyUint8Array.prototype.get = function LazyUint8Array_get(idx) {
if (idx > this.length-1 || idx < 0) {
return undefined;
}
@@ -1316,10 +1348,10 @@ mergeInto(LibraryManager.library, {
var chunkNum = Math.floor(idx / this.chunkSize);
return this.getter(chunkNum)[chunkOffset];
}
- LazyUint8Array.prototype.setDataGetter = function(getter) {
+ LazyUint8Array.prototype.setDataGetter = function LazyUint8Array_setDataGetter(getter) {
this.getter = getter;
}
- LazyUint8Array.prototype.cacheLength = function() {
+ LazyUint8Array.prototype.cacheLength = function LazyUint8Array_cacheLength() {
// Find length
var xhr = new XMLHttpRequest();
xhr.open('HEAD', url, false);
@@ -1415,7 +1447,7 @@ mergeInto(LibraryManager.library, {
var keys = Object.keys(node.stream_ops);
keys.forEach(function(key) {
var fn = node.stream_ops[key];
- stream_ops[key] = function() {
+ stream_ops[key] = function forceLoadLazyFile() {
if (!FS.forceLoadFile(node)) {
throw new FS.ErrnoError(ERRNO_CODES.EIO);
}
@@ -1423,7 +1455,7 @@ mergeInto(LibraryManager.library, {
};
});
// use a custom read function
- stream_ops.read = function(stream, buffer, offset, length, position) {
+ stream_ops.read = function stream_ops_read(stream, buffer, offset, length, position) {
if (!FS.forceLoadFile(node)) {
throw new FS.ErrnoError(ERRNO_CODES.EIO);
}
@@ -1462,7 +1494,7 @@ mergeInto(LibraryManager.library, {
Browser.init();
// TODO we should allow people to just pass in a complete filename instead
// of parent and name being that we just join them anyways
- var fullname = name ? PATH.resolve(PATH.join(parent, name)) : parent;
+ var fullname = name ? PATH.resolve(PATH.join2(parent, name)) : parent;
function processData(byteArray) {
function finish(byteArray) {
if (!dontCreateFile) {
@@ -1517,12 +1549,12 @@ mergeInto(LibraryManager.library, {
} catch (e) {
return onerror(e);
}
- openRequest.onupgradeneeded = function() {
+ openRequest.onupgradeneeded = function openRequest_onupgradeneeded() {
console.log('creating db');
var db = openRequest.result;
db.createObjectStore(FS.DB_STORE_NAME);
};
- openRequest.onsuccess = function() {
+ openRequest.onsuccess = function openRequest_onsuccess() {
var db = openRequest.result;
var transaction = db.transaction([FS.DB_STORE_NAME], 'readwrite');
var files = transaction.objectStore(FS.DB_STORE_NAME);
@@ -1532,8 +1564,8 @@ mergeInto(LibraryManager.library, {
}
paths.forEach(function(path) {
var putRequest = files.put(FS.analyzePath(path).object.contents, path);
- putRequest.onsuccess = function() { ok++; if (ok + fail == total) finish() };
- putRequest.onerror = function() { fail++; if (ok + fail == total) finish() };
+ putRequest.onsuccess = function putRequest_onsuccess() { ok++; if (ok + fail == total) finish() };
+ putRequest.onerror = function putRequest_onerror() { fail++; if (ok + fail == total) finish() };
});
transaction.onerror = onerror;
};
@@ -1551,7 +1583,7 @@ mergeInto(LibraryManager.library, {
return onerror(e);
}
openRequest.onupgradeneeded = onerror; // no database to load from
- openRequest.onsuccess = function() {
+ openRequest.onsuccess = function openRequest_onsuccess() {
var db = openRequest.result;
try {
var transaction = db.transaction([FS.DB_STORE_NAME], 'readonly');
@@ -1566,7 +1598,7 @@ mergeInto(LibraryManager.library, {
}
paths.forEach(function(path) {
var getRequest = files.get(path);
- getRequest.onsuccess = function() {
+ getRequest.onsuccess = function getRequest_onsuccess() {
if (FS.analyzePath(path).exists) {
FS.unlink(path);
}
@@ -1574,7 +1606,7 @@ mergeInto(LibraryManager.library, {
ok++;
if (ok + fail == total) finish();
};
- getRequest.onerror = function() { fail++; if (ok + fail == total) finish() };
+ getRequest.onerror = function getRequest_onerror() { fail++; if (ok + fail == total) finish() };
});
transaction.onerror = onerror;
};
diff --git a/src/library_gl.js b/src/library_gl.js
index 1ea8efc2..afd36197 100644
--- a/src/library_gl.js
+++ b/src/library_gl.js
@@ -11,6 +11,7 @@ var LibraryGL = {
#endif
counter: 1, // 0 is reserved as 'null' in gl
+ lastError: 0,
buffers: [],
programs: [],
framebuffers: [],
@@ -40,7 +41,13 @@ var LibraryGL = {
8 // GL_DOUBLE
],
- uniformTable: {}, // name => uniform ID. the uID must be identical until relinking, cannot create a new uID each call to glGetUniformLocation
+ programInfos: {}, // Stores additional information needed for each shader program. Each entry is of form:
+ /* { uniforms: {}, // Maps ints back to the opaque WebGLUniformLocation objects.
+ maxUniformLength: int, // Cached in order to implement glGetProgramiv(GL_ACTIVE_UNIFORM_MAX_LENGTH)
+ maxAttributeLength: int // Cached in order to implement glGetProgramiv(GL_ACTIVE_ATTRIBUTE_MAX_LENGTH)
+ } */
+
+ stringCache: {},
packAlignment: 4, // default alignment is 4 bytes
unpackAlignment: 4, // default alignment is 4 bytes
@@ -49,6 +56,13 @@ var LibraryGL = {
Browser.moduleContextCreatedCallbacks.push(GL.initExtensions);
},
+ // Records a GL error condition that occurred, stored until user calls glGetError() to fetch it. As per GLES2 spec, only the first error
+ // is remembered, and subsequent errors are discarded until the user has cleared the stored error by a call to glGetError().
+ recordError: function recordError(errorCode) {
+ if (!GL.lastError) {
+ GL.lastError = errorCode;
+ }
+ },
// Get a new ID for a texture/buffer/etc., while keeping the table dense and fast. Creation is farely rare so it is worth optimizing lookups later.
getNewId: function(table) {
var ret = GL.counter++;
@@ -240,7 +254,9 @@ var LibraryGL = {
sizePerPixel = 2;
break;
case 0x1406 /* GL_FLOAT */:
+#if ASSERTIONS
assert(GL.floatExt, 'Must have OES_texture_float to use float textures');
+#endif
switch (format) {
case 0x1907 /* GL_RGB */:
sizePerPixel = 3*4;
@@ -273,7 +289,7 @@ var LibraryGL = {
},
#if FULL_ES2
- calcBufLength: function(size, type, stride, count) {
+ calcBufLength: function calcBufLength(size, type, stride, count) {
if (stride > 0) {
return count * stride; // XXXvlad this is not exactly correct I don't think
}
@@ -283,7 +299,7 @@ var LibraryGL = {
usedTempBuffers: [],
- preDrawHandleClientVertexAttribBindings: function(count) {
+ preDrawHandleClientVertexAttribBindings: function preDrawHandleClientVertexAttribBindings(count) {
GL.resetBufferBinding = false;
var used = GL.usedTempBuffers;
@@ -317,7 +333,7 @@ var LibraryGL = {
}
},
- postDrawHandleClientVertexAttribBindings: function() {
+ postDrawHandleClientVertexAttribBindings: function postDrawHandleClientVertexAttribBindings() {
if (GL.resetBufferBinding) {
Module.ctx.bindBuffer(Module.ctx.ARRAY_BUFFER, GL.buffers[GL.currArrayBuffer]);
}
@@ -451,15 +467,23 @@ var LibraryGL = {
GL.validateGLObjectID(GL.programs, program, 'populateUniformTable', 'program');
#endif
var p = GL.programs[program];
- GL.uniformTable[program] = {};
- var ptable = GL.uniformTable[program];
- // A program's uniformTable maps the string name of an uniform to an integer location of that uniform.
+ GL.programInfos[program] = {
+ uniforms: {},
+ maxUniformLength: 0, // This is eagerly computed below, since we already enumerate all uniforms anyway.
+ maxAttributeLength: -1 // This is lazily computed and cached, computed when/if first asked, "-1" meaning not computed yet.
+ };
+
+ var ptable = GL.programInfos[program];
+ var utable = ptable.uniforms;
+ // A program's uniform table maps the string name of an uniform to an integer location of that uniform.
// The global GL.uniforms map maps integer locations to WebGLUniformLocations.
var numUniforms = Module.ctx.getProgramParameter(p, Module.ctx.ACTIVE_UNIFORMS);
for (var i = 0; i < numUniforms; ++i) {
var u = Module.ctx.getActiveUniform(p, i);
var name = u.name;
+ ptable.maxUniformLength = Math.max(ptable.maxUniformLength, name.length+1);
+
// Strip off any trailing array specifier we might have got, e.g. "[0]".
if (name.indexOf(']', name.length-1) !== -1) {
var ls = name.lastIndexOf('[');
@@ -467,11 +491,11 @@ var LibraryGL = {
}
// Optimize memory usage slightly: If we have an array of uniforms, e.g. 'vec3 colors[3];', then
- // only store the string 'colors' in ptable, and 'colors[0]', 'colors[1]' and 'colors[2]' will be parsed as 'colors'+i.
+ // only store the string 'colors' in utable, and 'colors[0]', 'colors[1]' and 'colors[2]' will be parsed as 'colors'+i.
// Note that for the GL.uniforms table, we still need to fetch the all WebGLUniformLocations for all the indices.
var loc = Module.ctx.getUniformLocation(p, name);
var id = GL.getNewId(GL.uniforms);
- ptable[name] = [u.size, id];
+ utable[name] = [u.size, id];
GL.uniforms[id] = loc;
for (var j = 1; j < u.size; ++j) {
@@ -497,11 +521,14 @@ var LibraryGL = {
glGetString__sig: 'ii',
glGetString: function(name_) {
+ if (GL.stringCache[name_]) return GL.stringCache[name_];
+ var ret;
switch(name_) {
case 0x1F00 /* GL_VENDOR */:
case 0x1F01 /* GL_RENDERER */:
case 0x1F02 /* GL_VERSION */:
- return allocate(intArrayFromString(Module.ctx.getParameter(name_)), 'i8', ALLOC_NORMAL);
+ ret = allocate(intArrayFromString(Module.ctx.getParameter(name_)), 'i8', ALLOC_NORMAL);
+ break;
case 0x1F03 /* GL_EXTENSIONS */:
var exts = Module.ctx.getSupportedExtensions();
var gl_exts = [];
@@ -509,12 +536,20 @@ var LibraryGL = {
gl_exts.push(exts[i]);
gl_exts.push("GL_" + exts[i]);
}
- return allocate(intArrayFromString(gl_exts.join(' ')), 'i8', ALLOC_NORMAL); // XXX this leaks! TODO: Cache all results like this in library_gl.js to be clean and nice and avoid leaking.
+ ret = allocate(intArrayFromString(gl_exts.join(' ')), 'i8', ALLOC_NORMAL);
+ break;
case 0x8B8C /* GL_SHADING_LANGUAGE_VERSION */:
- return allocate(intArrayFromString('OpenGL ES GLSL 1.00 (WebGL)'), 'i8', ALLOC_NORMAL);
+ 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;
},
glGetIntegerv__sig: 'vii',
@@ -523,6 +558,7 @@ var LibraryGL = {
case 0x8DFA: // GL_SHADER_COMPILER
{{{ makeSetValue('p', '0', '1', 'i32') }}};
return;
+ case 0x8DF8: // GL_SHADER_BINARY_FORMATS
case 0x8DF9: // GL_NUM_SHADER_BINARY_FORMATS
{{{ makeSetValue('p', '0', '0', 'i32') }}};
return;
@@ -542,7 +578,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') }}};
@@ -564,18 +604,45 @@ 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;
}
},
glGetFloatv__sig: 'vii',
glGetFloatv: function(name_, p) {
+ switch(name_) {
+ case 0x8DFA: // GL_SHADER_COMPILER
+ {{{ makeSetValue('p', '0', '1', 'float') }}};
+ return;
+ case 0x8DF8: // GL_SHADER_BINARY_FORMATS
+ GL.recordError(0x0500/*GL_INVALID_ENUM*/);
+#if GL_ASSERTIONS
+ Module.printErr('GL_INVALID_ENUM in glGetFloatv(GL_SHADER_BINARY_FORMATS): Invalid parameter type!');
+#endif
+ return;
+ case 0x8DF9: // GL_NUM_SHADER_BINARY_FORMATS
+ {{{ makeSetValue('p', '0', '0', 'float') }}};
+ return;
+ case 0x86A2: // GL_NUM_COMPRESSED_TEXTURE_FORMATS
+ // WebGL doesn't have GL_NUM_COMPRESSED_TEXTURE_FORMATS (it's obsolete since GL_COMPRESSED_TEXTURE_FORMATS returns a JS array that can be queried for length),
+ // so implement it ourselves to allow C++ GLES2 code get the length.
+ var formats = Module.ctx.getParameter(0x86A3 /*GL_COMPRESSED_TEXTURE_FORMATS*/);
+ {{{ makeSetValue('p', '0', 'formats.length', 'float') }}};
+ return;
+ }
+
var result = Module.ctx.getParameter(name_);
switch (typeof(result)) {
case "number":
@@ -588,7 +655,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 ||
@@ -607,18 +678,45 @@ 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;
}
},
glGetBooleanv__sig: 'vii',
glGetBooleanv: function(name_, p) {
+ switch(name_) {
+ case 0x8DFA: // GL_SHADER_COMPILER
+ {{{ makeSetValue('p', '0', '1', 'i8') }}};
+ return;
+ case 0x8DF8: // GL_SHADER_BINARY_FORMATS
+ GL.recordError(0x0500/*GL_INVALID_ENUM*/);
+#if GL_ASSERTIONS
+ Module.printErr('GL_INVALID_ENUM in glGetBooleanv(GL_SHADER_BINARY_FORMATS): Invalid parameter type!');
+#endif
+ return;
+ case 0x8DF9: // GL_NUM_SHADER_BINARY_FORMATS
+ {{{ makeSetValue('p', '0', '0', 'i8') }}};
+ return;
+ case 0x86A2: // GL_NUM_COMPRESSED_TEXTURE_FORMATS
+ // WebGL doesn't have GL_NUM_COMPRESSED_TEXTURE_FORMATS (it's obsolete since GL_COMPRESSED_TEXTURE_FORMATS returns a JS array that can be queried for length),
+ // so implement it ourselves to allow C++ GLES2 code get the length.
+ var hasCompressedFormats = Module.ctx.getParameter(0x86A3 /*GL_COMPRESSED_TEXTURE_FORMATS*/).length > 0 ? 1 : 0;
+ {{{ makeSetValue('p', '0', 'hasCompressedFormats', 'i8') }}};
+ return;
+ }
+
var result = Module.ctx.getParameter(name_);
switch (typeof(result)) {
case "number":
@@ -628,7 +726,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') }}};
@@ -646,13 +748,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;
}
},
@@ -680,7 +788,9 @@ var LibraryGL = {
glCompressedTexImage2D__sig: 'viiiiiiii',
glCompressedTexImage2D: function(target, level, internalFormat, width, height, border, imageSize, data) {
+#if ASSERTIONS
assert(GL.compressionExt);
+#endif
if (data) {
data = {{{ makeHEAPView('U8', 'data', 'data+imageSize') }}};
} else {
@@ -691,7 +801,9 @@ var LibraryGL = {
glCompressedTexSubImage2D__sig: 'viiiiiiiii',
glCompressedTexSubImage2D: function(target, level, xoffset, yoffset, width, height, format, imageSize, data) {
+#if ASSERTIONS
assert(GL.compressionExt);
+#endif
if (data) {
data = {{{ makeHEAPView('U8', 'data', 'data+imageSize') }}};
} else {
@@ -725,7 +837,9 @@ var LibraryGL = {
glReadPixels__sig: 'viiiiiii',
glReadPixels: function(x, y, width, height, format, type, pixels) {
+#if ASSERTIONS
assert(type == 0x1401 /* GL_UNSIGNED_BYTE */);
+#endif
var sizePerPixel;
switch (format) {
case 0x1907 /* GL_RGB */:
@@ -734,7 +848,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));
@@ -938,11 +1057,12 @@ var LibraryGL = {
name = name.slice(0, ls);
}
- var ptable = GL.uniformTable[program];
+ var ptable = GL.programInfos[program];
if (!ptable) {
return -1;
}
- var uniformInfo = ptable[name]; // returns pair [ dimension_of_uniform_array, uniform_location ]
+ var utable = ptable.uniforms;
+ var uniformInfo = utable[name]; // returns pair [ dimension_of_uniform_array, uniform_location ]
if (uniformInfo && arrayOffset < uniformInfo[0]) { // Check if user asked for an out-of-bounds element, i.e. for 'vec4 colors[3];' user could ask for 'colors[10]' which should return -1.
return uniformInfo[1]+arrayOffset;
} else {
@@ -1357,7 +1477,9 @@ var LibraryGL = {
{{{ makeSetValue('count', '0', 'len', 'i32') }}};
for (var i = 0; i < len; ++i) {
var id = GL.shaders.indexOf(result[i]);
+#if ASSERTIONS
assert(id !== -1, 'shader not bound to local id');
+#endif
{{{ makeSetValue('shaders', 'i*4', 'id', 'i32') }}};
}
},
@@ -1428,6 +1550,47 @@ var LibraryGL = {
#endif
if (pname == 0x8B84) { // GL_INFO_LOG_LENGTH
{{{ makeSetValue('p', '0', 'Module.ctx.getProgramInfoLog(GL.programs[program]).length + 1', 'i32') }}};
+ } else if (pname == 0x8B87 /* GL_ACTIVE_UNIFORM_MAX_LENGTH */) {
+ var ptable = GL.programInfos[program];
+ if (ptable) {
+ {{{ makeSetValue('p', '0', 'ptable.maxUniformLength', 'i32') }}};
+ return;
+ } else if (program < GL.counter) {
+#if GL_ASSERTIONS
+ Module.printErr("A GL object " + program + " that is not a program object was passed to glGetProgramiv!");
+#endif
+ GL.recordError(0x0502 /* GL_INVALID_OPERATION */);
+ } else {
+#if GL_ASSERTIONS
+ Module.printErr("A GL object " + program + " that did not come from GL was passed to glGetProgramiv!");
+#endif
+ GL.recordError(0x0501 /* GL_INVALID_VALUE */);
+ }
+ } else if (pname == 0x8B8A /* GL_ACTIVE_ATTRIBUTE_MAX_LENGTH */) {
+ var ptable = GL.programInfos[program];
+ if (ptable) {
+ if (ptable.maxAttributeLength == -1) {
+ var program = GL.programs[program];
+ var numAttribs = Module.ctx.getProgramParameter(program, Module.ctx.ACTIVE_ATTRIBUTES);
+ ptable.maxAttributeLength = 0; // Spec says if there are no active attribs, 0 must be returned.
+ for(var i = 0; i < numAttribs; ++i) {
+ var activeAttrib = Module.ctx.getActiveAttrib(program, i);
+ ptable.maxAttributeLength = Math.max(ptable.maxAttributeLength, activeAttrib.name.length+1);
+ }
+ }
+ {{{ makeSetValue('p', '0', 'ptable.maxAttributeLength', 'i32') }}};
+ return;
+ } else if (program < GL.counter) {
+#if GL_ASSERTIONS
+ Module.printErr("A GL object " + program + " that is not a program object was passed to glGetProgramiv!");
+#endif
+ GL.recordError(0x0502 /* GL_INVALID_OPERATION */);
+ } else {
+#if GL_ASSERTIONS
+ Module.printErr("A GL object " + program + " that did not come from GL was passed to glGetProgramiv!");
+#endif
+ GL.recordError(0x0501 /* GL_INVALID_VALUE */);
+ }
} else {
{{{ makeSetValue('p', '0', 'Module.ctx.getProgramParameter(GL.programs[program], pname)', 'i32') }}};
}
@@ -1455,7 +1618,7 @@ var LibraryGL = {
Module.ctx.deleteProgram(program);
program.name = 0;
GL.programs[program] = null;
- GL.uniformTable[program] = null;
+ GL.programInfos[program] = null;
},
glAttachShader__sig: 'vii',
@@ -1491,7 +1654,7 @@ var LibraryGL = {
GL.validateGLObjectID(GL.programs, program, 'glLinkProgram', 'program');
#endif
Module.ctx.linkProgram(GL.programs[program]);
- GL.uniformTable[program] = {}; // uniforms no longer keep the same names after linking
+ GL.programInfos[program] = null; // uniforms no longer keep the same names after linking
GL.populateUniformTable(program);
},
@@ -1663,7 +1826,7 @@ var LibraryGL = {
};
var glEnable = _glEnable;
- _glEnable = function(cap) {
+ _glEnable = function _glEnable(cap) {
// Clean up the renderer on any change to the rendering state. The optimization of
// skipping renderer setup is aimed at the case of multiple glDraw* right after each other
if (GL.immediate.lastRenderer) GL.immediate.lastRenderer.cleanup();
@@ -1685,7 +1848,7 @@ var LibraryGL = {
};
var glDisable = _glDisable;
- _glDisable = function(cap) {
+ _glDisable = function _glDisable(cap) {
if (GL.immediate.lastRenderer) GL.immediate.lastRenderer.cleanup();
if (cap == 0x0B60 /* GL_FOG */) {
GLEmulation.fogEnabled = false;
@@ -1703,7 +1866,7 @@ var LibraryGL = {
}
glDisable(cap);
};
- _glIsEnabled = function(cap) {
+ _glIsEnabled = function _glIsEnabled(cap) {
if (cap == 0x0B60 /* GL_FOG */) {
return GLEmulation.fogEnabled ? 1 : 0;
} else if (!(cap in validCapabilities)) {
@@ -1713,7 +1876,7 @@ var LibraryGL = {
};
var glGetBooleanv = _glGetBooleanv;
- _glGetBooleanv = function(pname, p) {
+ _glGetBooleanv = function _glGetBooleanv(pname, p) {
var attrib = GLEmulation.getAttributeFromCapability(pname);
if (attrib !== null) {
var result = GL.immediate.enabledClientAttributes[attrib];
@@ -1724,7 +1887,7 @@ var LibraryGL = {
};
var glGetIntegerv = _glGetIntegerv;
- _glGetIntegerv = function(pname, params) {
+ _glGetIntegerv = function _glGetIntegerv(pname, params) {
switch (pname) {
case 0x84E2: pname = Module.ctx.MAX_TEXTURE_IMAGE_UNITS /* fake it */; break; // GL_MAX_TEXTURE_UNITS
case 0x8B4A: { // GL_MAX_VERTEX_UNIFORM_COMPONENTS_ARB
@@ -1774,17 +1937,17 @@ var LibraryGL = {
return;
}
case 0x8088: { // GL_TEXTURE_COORD_ARRAY_SIZE
- var attribute = GLImmediate.clientAttributes[GLImmediate.TEXTURE0];
+ var attribute = GLImmediate.clientAttributes[GLImmediate.TEXTURE0 + GLImmediate.clientActiveTexture];
{{{ makeSetValue('params', '0', 'attribute ? attribute.size : 0', 'i32') }}};
return;
}
case 0x8089: { // GL_TEXTURE_COORD_ARRAY_TYPE
- var attribute = GLImmediate.clientAttributes[GLImmediate.TEXTURE0];
+ var attribute = GLImmediate.clientAttributes[GLImmediate.TEXTURE0 + GLImmediate.clientActiveTexture];
{{{ makeSetValue('params', '0', 'attribute ? attribute.type : 0', 'i32') }}};
return;
}
case 0x808A: { // GL_TEXTURE_COORD_ARRAY_STRIDE
- var attribute = GLImmediate.clientAttributes[GLImmediate.TEXTURE0];
+ var attribute = GLImmediate.clientAttributes[GLImmediate.TEXTURE0 + GLImmediate.clientActiveTexture];
{{{ makeSetValue('params', '0', 'attribute ? attribute.stride : 0', 'i32') }}};
return;
}
@@ -1793,14 +1956,17 @@ var LibraryGL = {
};
var glGetString = _glGetString;
- _glGetString = function(name_) {
+ _glGetString = function _glGetString(name_) {
+ if (GL.stringCache[name_]) return GL.stringCache[name_];
switch(name_) {
case 0x1F03 /* GL_EXTENSIONS */: // Add various extensions that we can support
- return allocate(intArrayFromString(Module.ctx.getSupportedExtensions().join(' ') +
+ var ret = allocate(intArrayFromString(Module.ctx.getSupportedExtensions().join(' ') +
' GL_EXT_texture_env_combine GL_ARB_texture_env_crossbar GL_ATI_texture_env_combine3 GL_NV_texture_env_combine4 GL_EXT_texture_env_dot3 GL_ARB_multitexture GL_ARB_vertex_buffer_object GL_EXT_framebuffer_object GL_ARB_vertex_program GL_ARB_fragment_program GL_ARB_shading_language_100 GL_ARB_shader_objects GL_ARB_vertex_shader GL_ARB_fragment_shader GL_ARB_texture_cube_map GL_EXT_draw_range_elements' +
(GL.compressionExt ? ' GL_ARB_texture_compression GL_EXT_texture_compression_s3tc' : '') +
(GL.anisotropicExt ? ' GL_EXT_texture_filter_anisotropic' : '')
), 'i8', ALLOC_NORMAL);
+ GL.stringCache[name_] = ret;
+ return ret;
}
return glGetString(name_);
};
@@ -1814,7 +1980,7 @@ var LibraryGL = {
GL.shaderOriginalSources = {};
#endif
var glCreateShader = _glCreateShader;
- _glCreateShader = function(shaderType) {
+ _glCreateShader = function _glCreateShader(shaderType) {
var id = glCreateShader(shaderType);
GL.shaderInfos[id] = {
type: shaderType,
@@ -1824,7 +1990,7 @@ var LibraryGL = {
};
var glShaderSource = _glShaderSource;
- _glShaderSource = function(shader, count, string, length) {
+ _glShaderSource = function _glShaderSource(shader, count, string, length) {
var source = GL.getSource(shader, count, string, length);
#if GL_DEBUG
console.log("glShaderSource: Input: \n" + source);
@@ -1937,7 +2103,7 @@ var LibraryGL = {
};
var glCompileShader = _glCompileShader;
- _glCompileShader = function(shader) {
+ _glCompileShader = function _glCompileShader(shader) {
Module.ctx.compileShader(GL.shaders[shader]);
#if GL_DEBUG
if (!Module.ctx.getShaderParameter(GL.shaders[shader], Module.ctx.COMPILE_STATUS)) {
@@ -1952,14 +2118,14 @@ var LibraryGL = {
GL.programShaders = {};
var glAttachShader = _glAttachShader;
- _glAttachShader = function(program, shader) {
+ _glAttachShader = function _glAttachShader(program, shader) {
if (!GL.programShaders[program]) GL.programShaders[program] = [];
GL.programShaders[program].push(shader);
glAttachShader(program, shader);
};
var glDetachShader = _glDetachShader;
- _glDetachShader = function(program, shader) {
+ _glDetachShader = function _glDetachShader(program, shader) {
var programShader = GL.programShaders[program];
if (!programShader) {
Module.printErr('WARNING: _glDetachShader received invalid program: ' + program);
@@ -1971,7 +2137,7 @@ var LibraryGL = {
};
var glUseProgram = _glUseProgram;
- _glUseProgram = function(program) {
+ _glUseProgram = function _glUseProgram(program) {
#if GL_DEBUG
if (GL.debug) {
Module.printErr('[using program with shaders]');
@@ -1988,7 +2154,7 @@ var LibraryGL = {
}
var glDeleteProgram = _glDeleteProgram;
- _glDeleteProgram = function(program) {
+ _glDeleteProgram = function _glDeleteProgram(program) {
glDeleteProgram(program);
if (program == GL.currProgram) GL.currProgram = 0;
};
@@ -1996,12 +2162,12 @@ var LibraryGL = {
// If attribute 0 was not bound, bind it to 0 for WebGL performance reasons. Track if 0 is free for that.
var zeroUsedPrograms = {};
var glBindAttribLocation = _glBindAttribLocation;
- _glBindAttribLocation = function(program, index, name) {
+ _glBindAttribLocation = function _glBindAttribLocation(program, index, name) {
if (index == 0) zeroUsedPrograms[program] = true;
glBindAttribLocation(program, index, name);
};
var glLinkProgram = _glLinkProgram;
- _glLinkProgram = function(program) {
+ _glLinkProgram = function _glLinkProgram(program) {
if (!(program in zeroUsedPrograms)) {
Module.ctx.bindAttribLocation(GL.programs[program], 0, 'a_position');
}
@@ -2009,11 +2175,13 @@ var LibraryGL = {
};
var glBindBuffer = _glBindBuffer;
- _glBindBuffer = function(target, buffer) {
+ _glBindBuffer = function _glBindBuffer(target, buffer) {
glBindBuffer(target, buffer);
if (target == Module.ctx.ARRAY_BUFFER) {
if (GLEmulation.currentVao) {
+#if ASSERTIONS
assert(GLEmulation.currentVao.arrayBuffer == buffer || GLEmulation.currentVao.arrayBuffer == 0 || buffer == 0, 'TODO: support for multiple array buffers in vao');
+#endif
GLEmulation.currentVao.arrayBuffer = buffer;
}
} else if (target == Module.ctx.ELEMENT_ARRAY_BUFFER) {
@@ -2022,7 +2190,7 @@ var LibraryGL = {
};
var glGetFloatv = _glGetFloatv;
- _glGetFloatv = function(pname, params) {
+ _glGetFloatv = function _glGetFloatv(pname, params) {
if (pname == 0x0BA6) { // GL_MODELVIEW_MATRIX
HEAPF32.set(GL.immediate.matrix['m'], params >> 2);
} else if (pname == 0x0BA7) { // GL_PROJECTION_MATRIX
@@ -2045,7 +2213,7 @@ var LibraryGL = {
};
var glHint = _glHint;
- _glHint = function(target, mode) {
+ _glHint = function _glHint(target, mode) {
if (target == 0x84EF) { // GL_TEXTURE_COMPRESSION_HINT
return;
}
@@ -2053,21 +2221,21 @@ var LibraryGL = {
};
var glEnableVertexAttribArray = _glEnableVertexAttribArray;
- _glEnableVertexAttribArray = function(index) {
+ _glEnableVertexAttribArray = function _glEnableVertexAttribArray(index) {
glEnableVertexAttribArray(index);
GLEmulation.enabledVertexAttribArrays[index] = 1;
if (GLEmulation.currentVao) GLEmulation.currentVao.enabledVertexAttribArrays[index] = 1;
};
var glDisableVertexAttribArray = _glDisableVertexAttribArray;
- _glDisableVertexAttribArray = function(index) {
+ _glDisableVertexAttribArray = function _glDisableVertexAttribArray(index) {
glDisableVertexAttribArray(index);
delete GLEmulation.enabledVertexAttribArrays[index];
if (GLEmulation.currentVao) delete GLEmulation.currentVao.enabledVertexAttribArrays[index];
};
var glVertexAttribPointer = _glVertexAttribPointer;
- _glVertexAttribPointer = function(index, size, type, normalized, stride, pointer) {
+ _glVertexAttribPointer = function _glVertexAttribPointer(index, size, type, normalized, stride, pointer) {
glVertexAttribPointer(index, size, type, normalized, stride, pointer);
if (GLEmulation.currentVao) { // TODO: avoid object creation here? likely not hot though
GLEmulation.currentVao.vertexAttribPointers[index] = [index, size, type, normalized, stride, pointer];
@@ -2099,9 +2267,6 @@ var LibraryGL = {
glGetShaderPrecisionFormat__sig: 'v',
glGetShaderPrecisionFormat: function() { throw 'glGetShaderPrecisionFormat: TODO' },
- glShaderBinary__sig: 'v',
- glShaderBinary: function() { throw 'glShaderBinary: TODO' },
-
glDeleteObject__sig: 'vi',
glDeleteObject: function(id) {
if (GL.programs[id]) {
@@ -2113,11 +2278,6 @@ var LibraryGL = {
}
},
- glReleaseShaderCompiler__sig: 'v',
- glReleaseShaderCompiler: function() {
- // NOP (as allowed by GLES 2.0 spec)
- },
-
glGetObjectParameteriv__sig: 'viii',
glGetObjectParameteriv: function(id, type, result) {
if (GL.programs[id]) {
@@ -2153,7 +2313,9 @@ var LibraryGL = {
glBindProgram__sig: 'vii',
glBindProgram: function(type, id) {
+#if ASSERTIONS
assert(id == 0);
+#endif
},
glGetPointerv: function(name, p) {
@@ -2164,8 +2326,13 @@ var LibraryGL = {
case 0x8090: // GL_COLOR_ARRAY_POINTER
attribute = GLImmediate.clientAttributes[GLImmediate.COLOR]; break;
case 0x8092: // GL_TEXTURE_COORD_ARRAY_POINTER
- attribute = GLImmediate.clientAttributes[GLImmediate.TEXTURE0]; break;
- default: throw 'TODO: glGetPointerv for ' + name;
+ attribute = GLImmediate.clientAttributes[GLImmediate.TEXTURE0 + GLImmediate.clientActiveTexture]; break;
+ 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') }}};
},
@@ -2186,14 +2353,14 @@ var LibraryGL = {
function CNaiveListMap() {
var list = [];
- this.insert = function(key, val) {
+ this.insert = function CNaiveListMap_insert(key, val) {
if (this.contains(key|0)) return false;
list.push([key, val]);
return true;
};
var __contains_i;
- this.contains = function(key) {
+ this.contains = function CNaiveListMap_contains(key) {
for (__contains_i = 0; __contains_i < list.length; ++__contains_i) {
if (list[__contains_i][0] === key) return true;
}
@@ -2201,7 +2368,7 @@ var LibraryGL = {
};
var __get_i;
- this.get = function(key) {
+ this.get = function CNaiveListMap_get(key) {
for (__get_i = 0; __get_i < list.length; ++__get_i) {
if (list[__get_i][0] === key) return list[__get_i][1];
}
@@ -2235,7 +2402,7 @@ var LibraryGL = {
function CNLNode() {
var map = new CNaiveListMap();
- this.child = function(keyFrag) {
+ this.child = function CNLNode_child(keyFrag) {
if (!map.contains(keyFrag|0)) {
map.insert(keyFrag|0, new CNLNode());
}
@@ -2243,11 +2410,11 @@ var LibraryGL = {
};
this.value = undefined;
- this.get = function() {
+ this.get = function CNLNode_get() {
return this.value;
};
- this.set = function(val) {
+ this.set = function CNLNode_set(val) {
this.value = val;
};
}
@@ -2255,22 +2422,22 @@ var LibraryGL = {
function CKeyView(root) {
var cur;
- this.reset = function() {
+ this.reset = function CKeyView_reset() {
cur = root;
return this;
};
this.reset();
- this.next = function(keyFrag) {
+ this.next = function CKeyView_next(keyFrag) {
cur = cur.child(keyFrag);
return this;
};
- this.get = function() {
+ this.get = function CKeyView_get() {
return cur.get();
};
- this.set = function(val) {
+ this.set = function CKeyView_set(val) {
cur.set(val);
};
};
@@ -2278,17 +2445,17 @@ var LibraryGL = {
var root;
var staticKeyView;
- this.createKeyView = function() {
+ this.createKeyView = function CNLNode_createKeyView() {
return new CKeyView(root);
}
- this.clear = function() {
+ this.clear = function CNLNode_clear() {
root = new CNLNode();
staticKeyView = this.createKeyView();
};
this.clear();
- this.getStaticKeyView = function() {
+ this.getStaticKeyView = function CNLNode_getStaticKeyView() {
staticKeyView.reset();
return staticKeyView;
};
@@ -2522,7 +2689,7 @@ var LibraryGL = {
GL_SRC_ALPHA
];
- this.traverseState = function(keyView) {
+ this.traverseState = function CTexEnv_traverseState(keyView) {
keyView.next(this.mode);
keyView.next(this.colorCombiner);
keyView.next(this.alphaCombiner);
@@ -2558,7 +2725,7 @@ var LibraryGL = {
this.enabled_tex3D = false;
this.enabled_texCube = false;
- this.traverseState = function(keyView) {
+ this.traverseState = function CTexUnit_traverseState(keyView) {
var texUnitType = this.getTexType();
keyView.next(texUnitType);
if (!texUnitType) return;
@@ -2567,11 +2734,11 @@ var LibraryGL = {
};
// Class impls:
- CTexUnit.prototype.enabled = function() {
+ CTexUnit.prototype.enabled = function CTexUnit_enabled() {
return this.getTexType() != 0;
}
- CTexUnit.prototype.genPassLines = function(passOutputVar, passInputVar, texUnitID) {
+ CTexUnit.prototype.genPassLines = function CTexUnit_genPassLines(passOutputVar, passInputVar, texUnitID) {
if (!this.enabled()) {
return ["vec4 " + passOutputVar + " = " + passInputVar + ";"];
}
@@ -2579,7 +2746,7 @@ var LibraryGL = {
return this.env.genPassLines(passOutputVar, passInputVar, texUnitID);
}
- CTexUnit.prototype.getTexType = function() {
+ CTexUnit.prototype.getTexType = function CTexUnit_getTexType() {
if (this.enabled_texCube) {
return GL_TEXTURE_CUBE_MAP;
} else if (this.enabled_tex3D) {
@@ -2592,7 +2759,7 @@ var LibraryGL = {
return 0;
}
- CTexEnv.prototype.genPassLines = function(passOutputVar, passInputVar, texUnitID) {
+ CTexEnv.prototype.genPassLines = function CTexEnv_genPassLines(passOutputVar, passInputVar, texUnitID) {
switch (this.mode) {
case GL_REPLACE: {
/* RGB:
@@ -2714,9 +2881,9 @@ var LibraryGL = {
return Abort_NoSupport("Unsupported TexEnv mode: 0x" + this.mode.toString(16));
}
- CTexEnv.prototype.genCombinerLines = function(isColor, outputVar,
- passInputVar, texUnitID,
- combiner, srcArr, opArr)
+ CTexEnv.prototype.genCombinerLines = function CTexEnv_getCombinerLines(isColor, outputVar,
+ passInputVar, texUnitID,
+ combiner, srcArr, opArr)
{
var argsNeeded = null;
switch (combiner) {
@@ -2832,9 +2999,9 @@ var LibraryGL = {
} else if (gl) {
maxTexUnits = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS);
}
-
+#if ASSERTIONS
assert(maxTexUnits > 0);
-
+#endif
s_texUnits = [];
for (var i = 0; i < maxTexUnits; i++) {
s_texUnits.push(new CTexUnit());
@@ -2893,9 +3060,10 @@ var LibraryGL = {
},
getTexUnitType: function(texUnitID) {
+#if ASSERTIONS
assert(texUnitID >= 0 &&
texUnitID < s_texUnits.length);
-
+#endif
return s_texUnits[texUnitID].getTexType();
},
@@ -3198,9 +3366,13 @@ var LibraryGL = {
if (!GL.immediate.enabledClientAttributes[texAttribName])
continue;
+#if ASSERTIONS
if (!useCurrProgram) {
- assert(GL.immediate.TexEnvJIT.getTexUnitType(i) != 0, "GL_TEXTURE" + i + " coords are supplied, but that texture unit is disabled in the fixed-function pipeline.");
+ if (GL.immediate.TexEnvJIT.getTexUnitType(i) == 0) {
+ Runtime.warnOnce("GL_TEXTURE" + i + " coords are supplied, but that texture unit is disabled in the fixed-function pipeline.");
+ }
}
+#endif
textureSizes[i] = GL.immediate.clientAttributes[texAttribName].size;
textureTypes[i] = GL.immediate.clientAttributes[texAttribName].type;
@@ -3417,7 +3589,9 @@ var LibraryGL = {
if (!GL.currArrayBuffer) {
var start = GL.immediate.firstVertex*GL.immediate.stride;
var end = GL.immediate.lastVertex*GL.immediate.stride;
+#if ASSERTIONS
assert(end <= GL.MAX_TEMP_BUFFER_SIZE, 'too much vertex data');
+#endif
arrayBuffer = GL.tempVertexBuffers[GL.tempBufferIndexLookup[end]];
// TODO: consider using the last buffer we bound, if it was larger. downside is larger buffer, but we might avoid rebinding and preparing
} else {
@@ -3565,7 +3739,7 @@ var LibraryGL = {
// Replace some functions with immediate-mode aware versions. If there are no client
// attributes enabled, and we use webgl-friendly modes (no GL_QUADS), then no need
// for emulation
- _glDrawArrays = function(mode, first, count) {
+ _glDrawArrays = function _glDrawArrays(mode, first, count) {
if (GL.immediate.totalEnabledClientAttributes == 0 && mode <= 6) {
Module.ctx.drawArrays(mode, first, count);
return;
@@ -3581,15 +3755,15 @@ var LibraryGL = {
GL.immediate.mode = -1;
};
- _glDrawElements = function(mode, count, type, indices, start, end) { // start, end are given if we come from glDrawRangeElements
+ _glDrawElements = function _glDrawElements(mode, count, type, indices, start, end) { // start, end are given if we come from glDrawRangeElements
if (GL.immediate.totalEnabledClientAttributes == 0 && mode <= 6 && GL.currElementArrayBuffer) {
Module.ctx.drawElements(mode, count, type, indices);
return;
}
+#if ASSERTIONS
if (!GL.currElementArrayBuffer) {
assert(type == Module.ctx.UNSIGNED_SHORT); // We can only emulate buffers of this kind, for now
}
-#if ASSERTIONS
console.log("DrawElements doesn't actually prepareClientAttributes properly.");
#endif
GL.immediate.prepareClientAttributes(count, false);
@@ -3621,43 +3795,43 @@ var LibraryGL = {
}
var glActiveTexture = _glActiveTexture;
- _glActiveTexture = function(texture) {
+ _glActiveTexture = function _glActiveTexture(texture) {
GL.immediate.TexEnvJIT.hook_activeTexture(texture);
glActiveTexture(texture);
};
var glEnable = _glEnable;
- _glEnable = function(cap) {
+ _glEnable = function _glEnable(cap) {
GL.immediate.TexEnvJIT.hook_enable(cap);
glEnable(cap);
};
var glDisable = _glDisable;
- _glDisable = function(cap) {
+ _glDisable = function _glDisable(cap) {
GL.immediate.TexEnvJIT.hook_disable(cap);
glDisable(cap);
};
var glTexEnvf = (typeof(_glTexEnvf) != 'undefined') ? _glTexEnvf : function(){};
- _glTexEnvf = function(target, pname, param) {
+ _glTexEnvf = function _glTexEnvf(target, pname, param) {
GL.immediate.TexEnvJIT.hook_texEnvf(target, pname, param);
// Don't call old func, since we are the implementor.
//glTexEnvf(target, pname, param);
};
var glTexEnvi = (typeof(_glTexEnvi) != 'undefined') ? _glTexEnvi : function(){};
- _glTexEnvi = function(target, pname, param) {
+ _glTexEnvi = function _glTexEnvi(target, pname, param) {
GL.immediate.TexEnvJIT.hook_texEnvi(target, pname, param);
// Don't call old func, since we are the implementor.
//glTexEnvi(target, pname, param);
};
var glTexEnvfv = (typeof(_glTexEnvfv) != 'undefined') ? _glTexEnvfv : function(){};
- _glTexEnvfv = function(target, pname, param) {
+ _glTexEnvfv = function _glTexEnvfv(target, pname, param) {
GL.immediate.TexEnvJIT.hook_texEnvfv(target, pname, param);
// Don't call old func, since we are the implementor.
//glTexEnvfv(target, pname, param);
};
var glGetIntegerv = _glGetIntegerv;
- _glGetIntegerv = function(pname, params) {
+ _glGetIntegerv = function _glGetIntegerv(pname, params) {
switch (pname) {
case 0x8B8D: { // GL_CURRENT_PROGRAM
// Just query directly so we're working with WebGL objects.
@@ -3797,13 +3971,17 @@ var LibraryGL = {
if (!attribute) break;
attribute.offset = attribute.pointer - start;
if (attribute.offset > bytes) { // ensure we start where we should
+#if ASSERTIONS
assert((attribute.offset - bytes)%4 == 0); // XXX assuming 4-alignment
+#endif
bytes += attribute.offset - bytes;
}
bytes += attribute.size * GL.byteSizeByType[attribute.type - GL.byteSizeByTypeRoot];
if (bytes % 4 != 0) bytes += 4 - (bytes % 4); // XXX assuming 4-alignment
}
+#if ASSERTIONS
assert(beginEnd || bytes <= stride); // if not begin-end, explicit stride should make sense with total byte size
+#endif
if (bytes < stride) { // ensure the size is that of the stride
bytes = stride;
}
@@ -3830,18 +4008,21 @@ var LibraryGL = {
// Generate index data in a format suitable for GLES 2.0/WebGL
var numVertexes = 4 * this.vertexCounter / GL.immediate.stride;
+#if ASSERTIONS
assert(numVertexes % 1 == 0, "`numVertexes` must be an integer.");
-
+#endif
var emulatedElementArrayBuffer = false;
var numIndexes = 0;
if (numProvidedIndexes) {
numIndexes = numProvidedIndexes;
if (!GL.currArrayBuffer && GL.immediate.firstVertex > GL.immediate.lastVertex) {
// Figure out the first and last vertex from the index data
+#if ASSERTIONS
assert(!GL.currElementArrayBuffer); // If we are going to upload array buffer data, we need to find which range to
// upload based on the indices. If they are in a buffer on the GPU, that is very
// inconvenient! So if you do not have an array buffer, you should also not have
// an element array buffer. But best is to use both buffers!
+#endif
for (var i = 0; i < numProvidedIndexes; i++) {
var currIndex = {{{ makeGetValue('ptr', 'i*2', 'i16', null, 1) }}};
GL.immediate.firstVertex = Math.min(GL.immediate.firstVertex, currIndex);
@@ -3850,7 +4031,9 @@ var LibraryGL = {
}
if (!GL.currElementArrayBuffer) {
// If no element array buffer is bound, then indices is a literal pointer to clientside data
+#if ASSERTIONS
assert(numProvidedIndexes << 1 <= GL.MAX_TEMP_BUFFER_SIZE, 'too many immediate mode indexes (a)');
+#endif
var indexBuffer = GL.tempIndexBuffers[GL.tempBufferIndexLookup[numProvidedIndexes << 1]];
Module.ctx.bindBuffer(Module.ctx.ELEMENT_ARRAY_BUFFER, indexBuffer);
Module.ctx.bufferSubData(Module.ctx.ELEMENT_ARRAY_BUFFER, 0, {{{ makeHEAPView('U16', 'ptr', 'ptr + (numProvidedIndexes << 1)') }}});
@@ -3862,11 +4045,15 @@ var LibraryGL = {
// GL.immediate.firstVertex is the first vertex we want. Quad indexes are in the pattern
// 0 1 2, 0 2 3, 4 5 6, 4 6 7, so we need to look at index firstVertex * 1.5 to see it.
// Then since indexes are 2 bytes each, that means 3
+#if ASSERTIONS
assert(GL.immediate.firstVertex % 4 == 0);
+#endif
ptr = GL.immediate.firstVertex*3;
var numQuads = numVertexes / 4;
numIndexes = numQuads * 6; // 0 1 2, 0 2 3 pattern
+#if ASSERTIONS
assert(ptr + (numIndexes << 1) <= GL.MAX_TEMP_BUFFER_SIZE, 'too many immediate mode indexes (b)');
+#endif
Module.ctx.bindBuffer(Module.ctx.ELEMENT_ARRAY_BUFFER, GL.tempQuadIndexBuffer);
emulatedElementArrayBuffer = true;
}
@@ -4572,6 +4759,30 @@ var LibraryGL = {
#endif
},
+ glShaderBinary__sig: 'v',
+ glShaderBinary: function() {
+ GL.recordError(0x0500/*GL_INVALID_ENUM*/);
+#if GL_ASSERTIONS
+ Module.printErr("GL_INVALID_ENUM in glShaderBinary: WebGL does not support binary shader formats! Calls to glShaderBinary always fail.");
+#endif
+ },
+
+ glReleaseShaderCompiler__sig: 'v',
+ glReleaseShaderCompiler: function() {
+ // NOP (as allowed by GLES 2.0 spec)
+ },
+
+ glGetError__sig: 'i',
+ glGetError: function() {
+ // First return any GL error generated by the emscripten library_gl.js interop layer.
+ if (GL.lastError) {
+ var error = GL.lastError;
+ GL.lastError = 0/*GL_NO_ERROR*/;
+ return error;
+ } else { // If there were none, return the GL error from the browser GL context.
+ return Module.ctx.getError();
+ }
+ },
// signatures of simple pass-through functions, see later
glActiveTexture__sig: 'vi',
@@ -4605,14 +4816,13 @@ var LibraryGL = {
glFlush__sig: 'v',
glClearColor__sig: 'viiii',
glIsEnabled__sig: 'ii',
- glGetError__sig: 'i',
glFrontFace__sig: 'vi',
glSampleCoverage__sig: 'vi',
};
// Simple pass-through functions. Starred ones have return values. [X] ones have X in the C name but not in the JS name
-[[0, 'getError* finish flush'],
+[[0, 'finish flush'],
[1, 'clearDepth clearDepth[f] depthFunc enable disable frontFace cullFace clear lineWidth clearStencil depthMask stencilMask checkFramebufferStatus* generateMipmap activeTexture blendEquation sampleCoverage isEnabled*'],
[2, 'blendFunc blendEquationSeparate depthRange depthRange[f] stencilMaskSeparate hint polygonOffset vertexAttrib1f'],
[3, 'texParameteri texParameterf vertexAttrib2f stencilFunc stencilOp'],
@@ -4687,7 +4897,7 @@ LibraryGL.emscripten_GetProcAddress__deps = [function() {
tableImpl += '}\nreturn 0;';
LibraryManager.library.emscripten_procAddressTable = new Function('name', tableImpl);
}, 'emscripten_procAddressTable'];
-LibraryGL.emscripten_GetProcAddress = function(name) {
+LibraryGL.emscripten_GetProcAddress = function _LibraryGL_emscripten_GetProcAddress(name) {
name = name.replace('EXT', '').replace('ARB', '');
switch(name) { // misc renamings
case 'glCreateProgramObject': name = 'glCreateProgram'; break;
diff --git a/src/library_glfw.js b/src/library_glfw.js
index b0519e39..647d4bb6 100644
--- a/src/library_glfw.js
+++ b/src/library_glfw.js
@@ -355,7 +355,9 @@ var LibraryGLFW = {
}
var contextAttributes = {
- antialias: (GLFW.params[0x00020013] > 1) //GLFW_FSAA_SAMPLES
+ antialias: (GLFW.params[0x00020013] > 1), //GLFW_FSAA_SAMPLES
+ depth: (GLFW.params[0x00020009] > 0), //GLFW_DEPTH_BITS
+ stencil: (GLFW.params[0x0002000A] > 0) //GLFW_STENCIL_BITS
}
Module.ctx = Browser.createContext(Module['canvas'], true, true, contextAttributes);
return 1; //GL_TRUE
diff --git a/src/library_glut.js b/src/library_glut.js
index 722ea85c..ba4d75ab 100644
--- a/src/library_glut.js
+++ b/src/library_glut.js
@@ -59,6 +59,9 @@ var LibraryGLUT = {
getSpecialKey: function(keycode) {
var key = null;
switch (keycode) {
+ case 8: key = 120 /* backspace */; break;
+ case 46: key = 111 /* delete */; break;
+
case 0x70 /*DOM_VK_F1*/: key = 1 /* GLUT_KEY_F1 */; break;
case 0x71 /*DOM_VK_F2*/: key = 2 /* GLUT_KEY_F2 */; break;
case 0x72 /*DOM_VK_F3*/: key = 3 /* GLUT_KEY_F3 */; break;
@@ -228,14 +231,14 @@ var LibraryGLUT = {
if (delta < 0) {
button = 4; // wheel down
}
-
+
if (GLUT.mouseFunc) {
event.preventDefault();
GLUT.saveModifiers(event);
Runtime.dynCall('viiii', GLUT.mouseFunc, [button, 0/*GLUT_DOWN*/, Browser.mouseX, Browser.mouseY]);
}
},
-
+
// TODO add fullscreen API ala:
// http://johndyer.name/native-fullscreen-javascript-api-plus-jquery-plugin/
onFullScreenEventChange: function(event) {
@@ -304,7 +307,7 @@ var LibraryGLUT = {
// Firefox
window.addEventListener("DOMMouseScroll", GLUT.onMouseWheel, true);
}
-
+
Browser.resizeListeners.push(function(width, height) {
if (GLUT.reshapeFunc) {
Runtime.dynCall('vii', GLUT.reshapeFunc, [width, height]);
@@ -372,7 +375,7 @@ var LibraryGLUT = {
},
glutIdleFunc: function(func) {
- var callback = function() {
+ function callback() {
if (GLUT.idleFunc) {
Runtime.dynCall('v', GLUT.idleFunc);
Browser.safeSetTimeout(callback, 0);
@@ -427,7 +430,9 @@ var LibraryGLUT = {
glutCreateWindow__deps: ['$Browser'],
glutCreateWindow: function(name) {
var contextAttributes = {
- antialias: ((GLUT.initDisplayMode & 0x0080 /*GLUT_MULTISAMPLE*/) != 0)
+ antialias: ((GLUT.initDisplayMode & 0x0080 /*GLUT_MULTISAMPLE*/) != 0),
+ depth: ((GLUT.initDisplayMode & 0x0010 /*GLUT_DEPTH*/) != 0),
+ stencil: ((GLUT.initDisplayMode & 0x0020 /*GLUT_STENCIL*/) != 0)
};
Module.ctx = Browser.createContext(Module['canvas'], true, true, contextAttributes);
return Module.ctx ? 1 /* a new GLUT window ID for the created context */ : 0 /* failure */;
diff --git a/src/library_idbfs.js b/src/library_idbfs.js
index 9031bad8..7f50f17e 100644
--- a/src/library_idbfs.js
+++ b/src/library_idbfs.js
@@ -58,7 +58,7 @@ mergeInto(LibraryManager.library, {
}
var completed = 0;
- var done = function(err) {
+ function done(err) {
if (err) return callback(err);
if (++completed >= total) {
return callback(null);
@@ -68,7 +68,7 @@ mergeInto(LibraryManager.library, {
// create a single transaction to handle and IDB reads / writes we'll need to do
var db = src.type === 'remote' ? src.db : dst.db;
var transaction = db.transaction([IDBFS.DB_STORE_NAME], 'readwrite');
- transaction.onerror = function() { callback(this.error); };
+ transaction.onerror = function transaction_onerror() { callback(this.error); };
var store = transaction.objectStore(IDBFS.DB_STORE_NAME);
for (var path in create) {
@@ -92,8 +92,8 @@ mergeInto(LibraryManager.library, {
} else {
// save file to IDB
var req = store.put(entry, path);
- req.onsuccess = function() { done(null); };
- req.onerror = function() { done(this.error); };
+ req.onsuccess = function req_onsuccess() { done(null); };
+ req.onerror = function req_onerror() { done(this.error); };
}
}
@@ -117,20 +117,20 @@ mergeInto(LibraryManager.library, {
} else {
// delete file from IDB
var req = store.delete(path);
- req.onsuccess = function() { done(null); };
- req.onerror = function() { done(this.error); };
+ req.onsuccess = function req_onsuccess() { done(null); };
+ req.onerror = function req_onerror() { done(this.error); };
}
}
},
getLocalSet: function(mount, callback) {
var files = {};
- var isRealDir = function(p) {
+ function isRealDir(p) {
return p !== '.' && p !== '..';
};
- var toAbsolute = function(root) {
+ function toAbsolute(root) {
return function(p) {
- return PATH.join(root, p);
+ return PATH.join2(root, p);
}
};
@@ -177,17 +177,17 @@ mergeInto(LibraryManager.library, {
} catch (e) {
return onerror(e);
}
- req.onupgradeneeded = function() {
+ req.onupgradeneeded = function req_onupgradeneeded() {
db = req.result;
db.createObjectStore(IDBFS.DB_STORE_NAME);
};
- req.onsuccess = function() {
+ req.onsuccess = function req_onsuccess() {
db = req.result;
// add to the cache
IDBFS.dbs[name] = db;
callback(null, db);
};
- req.onerror = function() {
+ req.onerror = function req_onerror() {
callback(this.error);
};
},
@@ -198,10 +198,10 @@ mergeInto(LibraryManager.library, {
if (err) return callback(err);
var transaction = db.transaction([IDBFS.DB_STORE_NAME], 'readonly');
- transaction.onerror = function() { callback(this.error); };
+ transaction.onerror = function transaction_onerror() { callback(this.error); };
var store = transaction.objectStore(IDBFS.DB_STORE_NAME);
- store.openCursor().onsuccess = function(event) {
+ store.openCursor().onsuccess = function store_openCursor_onsuccess(event) {
var cursor = event.target.result;
if (!cursor) {
return callback(null, { type: 'remote', db: db, files: files });
diff --git a/src/library_memfs.js b/src/library_memfs.js
index 94fd767e..d3148d8b 100644
--- a/src/library_memfs.js
+++ b/src/library_memfs.js
@@ -1,6 +1,8 @@
mergeInto(LibraryManager.library, {
$MEMFS__deps: ['$FS'],
$MEMFS: {
+ ops_table: null,
+
// content modes
CONTENT_OWNING: 1, // contains a subarray into the heap, and we own it, without copying (note: someone else needs to free() it, if that is necessary)
CONTENT_FLEXIBLE: 2, // has been modified or never set to anything, and is a flexible js array that can grow/shrink
@@ -13,51 +15,71 @@ mergeInto(LibraryManager.library, {
// no supported
throw new FS.ErrnoError(ERRNO_CODES.EPERM);
}
+ if (!MEMFS.ops_table) {
+ MEMFS.ops_table = {
+ dir: {
+ node: {
+ getattr: MEMFS.node_ops.getattr,
+ setattr: MEMFS.node_ops.setattr,
+ lookup: MEMFS.node_ops.lookup,
+ mknod: MEMFS.node_ops.mknod,
+ mknod: MEMFS.node_ops.mknod,
+ rename: MEMFS.node_ops.rename,
+ unlink: MEMFS.node_ops.unlink,
+ rmdir: MEMFS.node_ops.rmdir,
+ readdir: MEMFS.node_ops.readdir,
+ symlink: MEMFS.node_ops.symlink
+ },
+ stream: {
+ llseek: MEMFS.stream_ops.llseek
+ }
+ },
+ file: {
+ node: {
+ getattr: MEMFS.node_ops.getattr,
+ setattr: MEMFS.node_ops.setattr
+ },
+ stream: {
+ llseek: MEMFS.stream_ops.llseek,
+ read: MEMFS.stream_ops.read,
+ write: MEMFS.stream_ops.write,
+ allocate: MEMFS.stream_ops.allocate,
+ mmap: MEMFS.stream_ops.mmap
+ }
+ },
+ link: {
+ node: {
+ getattr: MEMFS.node_ops.getattr,
+ setattr: MEMFS.node_ops.setattr,
+ readlink: MEMFS.node_ops.readlink
+ },
+ stream: {}
+ },
+ chrdev: {
+ node: {
+ getattr: MEMFS.node_ops.getattr,
+ setattr: MEMFS.node_ops.setattr
+ },
+ stream: FS.chrdev_stream_ops
+ },
+ };
+ }
var node = FS.createNode(parent, name, mode, dev);
if (FS.isDir(node.mode)) {
- node.node_ops = {
- getattr: MEMFS.node_ops.getattr,
- setattr: MEMFS.node_ops.setattr,
- lookup: MEMFS.node_ops.lookup,
- mknod: MEMFS.node_ops.mknod,
- mknod: MEMFS.node_ops.mknod,
- rename: MEMFS.node_ops.rename,
- unlink: MEMFS.node_ops.unlink,
- rmdir: MEMFS.node_ops.rmdir,
- readdir: MEMFS.node_ops.readdir,
- symlink: MEMFS.node_ops.symlink
- };
- node.stream_ops = {
- llseek: MEMFS.stream_ops.llseek
- };
+ node.node_ops = MEMFS.ops_table.dir.node;
+ node.stream_ops = MEMFS.ops_table.dir.stream;
node.contents = {};
} else if (FS.isFile(node.mode)) {
- node.node_ops = {
- getattr: MEMFS.node_ops.getattr,
- setattr: MEMFS.node_ops.setattr
- };
- node.stream_ops = {
- llseek: MEMFS.stream_ops.llseek,
- read: MEMFS.stream_ops.read,
- write: MEMFS.stream_ops.write,
- allocate: MEMFS.stream_ops.allocate,
- mmap: MEMFS.stream_ops.mmap
- };
+ node.node_ops = MEMFS.ops_table.file.node;
+ node.stream_ops = MEMFS.ops_table.file.stream;
node.contents = [];
node.contentMode = MEMFS.CONTENT_FLEXIBLE;
} else if (FS.isLink(node.mode)) {
- node.node_ops = {
- getattr: MEMFS.node_ops.getattr,
- setattr: MEMFS.node_ops.setattr,
- readlink: MEMFS.node_ops.readlink
- };
- node.stream_ops = {};
+ node.node_ops = MEMFS.ops_table.link.node;
+ node.stream_ops = MEMFS.ops_table.link.stream;
} else if (FS.isChrdev(node.mode)) {
- node.node_ops = {
- getattr: MEMFS.node_ops.getattr,
- setattr: MEMFS.node_ops.setattr
- };
- node.stream_ops = FS.chrdev_stream_ops;
+ node.node_ops = MEMFS.ops_table.chrdev.node;
+ node.stream_ops = MEMFS.ops_table.chrdev.stream;
}
node.timestamp = Date.now();
// add the new node to the parent
@@ -117,7 +139,7 @@ mergeInto(LibraryManager.library, {
}
},
lookup: function(parent, name) {
- throw new FS.ErrnoError(ERRNO_CODES.ENOENT);
+ throw FS.genericErrors[ERRNO_CODES.ENOENT];
},
mknod: function(parent, name, mode, dev) {
return MEMFS.createNode(parent, name, mode, dev);
@@ -200,10 +222,12 @@ mergeInto(LibraryManager.library, {
#if USE_TYPED_ARRAYS == 2
if (length && contents.length === 0 && position === 0 && buffer.subarray) {
// just replace it with the new data
+#if ASSERTIONS
assert(buffer.length);
- if (canOwn && buffer.buffer === HEAP8.buffer && offset === 0) {
- node.contents = buffer; // this is a subarray of the heap, and we can own it
- node.contentMode = MEMFS.CONTENT_OWNING;
+#endif
+ if (canOwn && offset === 0) {
+ node.contents = buffer; // this could be a subarray of Emscripten HEAP, or allocated from some other source.
+ node.contentMode = (buffer.buffer === HEAP8.buffer) ? MEMFS.CONTENT_OWNING : MEMFS.CONTENT_FIXED;
} else {
node.contents = new Uint8Array(buffer.subarray(offset, offset+length));
node.contentMode = MEMFS.CONTENT_FIXED;
diff --git a/src/library_nodefs.js b/src/library_nodefs.js
index 2be54076..7686f3f2 100644
--- a/src/library_nodefs.js
+++ b/src/library_nodefs.js
@@ -134,7 +134,7 @@ mergeInto(LibraryManager.library, {
}
},
lookup: function (parent, name) {
- var path = PATH.join(NODEFS.realPath(parent), name);
+ var path = PATH.join2(NODEFS.realPath(parent), name);
var mode = NODEFS.getMode(path);
return NODEFS.createNode(parent, name, mode);
},
@@ -156,7 +156,7 @@ mergeInto(LibraryManager.library, {
},
rename: function (oldNode, newDir, newName) {
var oldPath = NODEFS.realPath(oldNode);
- var newPath = PATH.join(NODEFS.realPath(newDir), newName);
+ var newPath = PATH.join2(NODEFS.realPath(newDir), newName);
try {
fs.renameSync(oldPath, newPath);
} catch (e) {
@@ -165,7 +165,7 @@ mergeInto(LibraryManager.library, {
}
},
unlink: function(parent, name) {
- var path = PATH.join(NODEFS.realPath(parent), name);
+ var path = PATH.join2(NODEFS.realPath(parent), name);
try {
fs.unlinkSync(path);
} catch (e) {
@@ -174,7 +174,7 @@ mergeInto(LibraryManager.library, {
}
},
rmdir: function(parent, name) {
- var path = PATH.join(NODEFS.realPath(parent), name);
+ var path = PATH.join2(NODEFS.realPath(parent), name);
try {
fs.rmdirSync(path);
} catch (e) {
@@ -192,7 +192,7 @@ mergeInto(LibraryManager.library, {
}
},
symlink: function(parent, newName, oldPath) {
- var newPath = PATH.join(NODEFS.realPath(parent), newName);
+ var newPath = PATH.join2(NODEFS.realPath(parent), newName);
try {
fs.symlinkSync(oldPath, newPath);
} catch (e) {
@@ -283,4 +283,4 @@ mergeInto(LibraryManager.library, {
}
}
}
-}); \ No newline at end of file
+});
diff --git a/src/library_openal.js b/src/library_openal.js
index e8a2e223..eb152f62 100644
--- a/src/library_openal.js
+++ b/src/library_openal.js
@@ -8,13 +8,13 @@ var LibraryOpenAL = {
QUEUE_INTERVAL: 25,
QUEUE_LOOKAHEAD: 100,
- updateSources: function(context) {
+ updateSources: function updateSources(context) {
for (var i = 0; i < context.src.length; i++) {
AL.updateSource(context.src[i]);
}
},
- updateSource: function(src) {
+ updateSource: function updateSource(src) {
#if OPENAL_DEBUG
var idx = AL.currentContext.src.indexOf(src);
#endif
@@ -65,7 +65,7 @@ var LibraryOpenAL = {
}
},
- setSourceState: function(src, state) {
+ setSourceState: function setSourceState(src, state) {
#if OPENAL_DEBUG
var idx = AL.currentContext.src.indexOf(src);
#endif
@@ -119,7 +119,7 @@ var LibraryOpenAL = {
}
},
- stopSourceQueue: function(src) {
+ stopSourceQueue: function stopSourceQueue(src) {
for (var i = 0; i < src.queue.length; i++) {
var entry = src.queue[i];
if (entry.src) {
diff --git a/src/library_path.js b/src/library_path.js
index 09808acd..f00a7586 100644
--- a/src/library_path.js
+++ b/src/library_path.js
@@ -59,26 +59,22 @@ mergeInto(LibraryManager.library, {
}
return root + dir;
},
- basename: function(path, ext) {
+ basename: function(path) {
// EMSCRIPTEN return '/'' for '/', not an empty string
if (path === '/') return '/';
- var f = PATH.splitPath(path)[2];
- if (ext && f.substr(-1 * ext.length) === ext) {
- f = f.substr(0, f.length - ext.length);
- }
- return f;
+ var lastSlash = path.lastIndexOf('/');
+ if (lastSlash === -1) return path;
+ return path.substr(lastSlash+1);
},
extname: function(path) {
return PATH.splitPath(path)[3];
},
join: function() {
var paths = Array.prototype.slice.call(arguments, 0);
- return PATH.normalize(paths.filter(function(p, index) {
- if (typeof p !== 'string') {
- throw new TypeError('Arguments to path.join must be strings');
- }
- return p;
- }).join('/'));
+ return PATH.normalize(paths.join('/'));
+ },
+ join2: function(l, r) {
+ return PATH.normalize(l + '/' + r);
},
resolve: function() {
var resolvedPath = '',
@@ -134,4 +130,4 @@ mergeInto(LibraryManager.library, {
return outputParts.join('/');
}
}
-}); \ No newline at end of file
+});
diff --git a/src/library_sdl.js b/src/library_sdl.js
index a0689343..8fd06a43 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],
@@ -153,24 +154,30 @@ var LibrarySDL = {
120: 27,
121: 28,
122: 29, // Z
- 44: 54, // comma
- 46: 55, // period
- 47: 56, // slash
- 49: 30, // 1
- 50: 31,
- 51: 32,
- 52: 33,
- 53: 34,
- 54: 35,
- 55: 36,
- 56: 37,
- 57: 38, // 9
- 48: 39, // 0
- 13: 40, // return
- 9: 43, // tab
- 27: 41, // escape
- 32: 44, // space
- 92: 49, // backslash
+ 49: 30, // 1
+ 50: 31,
+ 51: 32,
+ 52: 33,
+ 53: 34,
+ 54: 35,
+ 55: 36,
+ 56: 37,
+ 57: 38, // 9
+ 48: 39, // 0
+ 13: 40, // return
+ 27: 41, // escape
+ 8: 42, // backspace
+ 9: 43, // tab
+ 32: 44, // space
+ 61: 46, // equals
+ 91: 47, // left bracket
+ 93: 48, // right bracket
+ 92: 49, // backslash
+ 59: 51, // ;
+ 96: 52, // apostrophe
+ 44: 54, // comma
+ 46: 55, // period
+ 47: 56, // slash
305: 224, // ctrl
308: 226, // alt
},
@@ -254,9 +261,13 @@ var LibrarySDL = {
}
var webGLContextAttributes = {
- antialias: ((SDL.glAttributes[13 /*SDL_GL_MULTISAMPLEBUFFERS*/] != 0) && (SDL.glAttributes[14 /*SDL_GL_MULTISAMPLESAMPLES*/] > 1))
+ antialias: ((SDL.glAttributes[13 /*SDL_GL_MULTISAMPLEBUFFERS*/] != 0) && (SDL.glAttributes[14 /*SDL_GL_MULTISAMPLESAMPLES*/] > 1)),
+ depth: (SDL.glAttributes[6 /*SDL_GL_DEPTH_SIZE*/] > 0),
+ stencil: (SDL.glAttributes[7 /*SDL_GL_STENCIL_SIZE*/] > 0)
};
+
var ctx = Browser.createContext(canvas, useWebGL, usePageCanvas, webGLContextAttributes);
+
SDL.surfaces[surf] = {
width: width,
height: height,
@@ -629,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;
}
},
@@ -685,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: 1, // SDL_ENABLE
+ 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() {
@@ -698,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);
@@ -708,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);
@@ -720,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
},
@@ -786,6 +931,14 @@ var LibrarySDL = {
['mousedown', 'mouseup', 'mousemove', 'DOMMouseScroll', 'mousewheel', 'mouseout'].forEach(function(event) {
Module['canvas'].addEventListener(event, SDL.receiveEvent, true);
});
+
+ // (0,0) means 'use fullscreen' in native; in Emscripten, use the current canvas size.
+ if (width == 0 && height == 0) {
+ var canvas = Module['canvas'];
+ width = canvas.width;
+ height = canvas.height;
+ }
+
Browser.setCanvasSize(width, height, true);
// Free the old surface first.
if (SDL.screen) {
@@ -1171,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);
@@ -1219,11 +1377,11 @@ var LibrarySDL = {
surfData.colors = new Uint8Array(256 * 3); //256 RGB colors
}
- for (var i = firstColor; i < firstColor + nColors; i++) {
- var index = i *3;
+ for (var i = 0; i < nColors; ++i) {
+ var index = (firstColor + i) * 3;
surfData.colors[index] = {{{ makeGetValue('colors', 'i*4', 'i8', null, true) }}};
- surfData.colors[index +1] = {{{ makeGetValue('colors', 'i*4 +1', 'i8', null, true) }}};
- surfData.colors[index +2] = {{{ makeGetValue('colors', 'i*4 +2', 'i8', null, true) }}};
+ surfData.colors[index + 1] = {{{ makeGetValue('colors', 'i*4 + 1', 'i8', null, true) }}};
+ surfData.colors[index + 2] = {{{ makeGetValue('colors', 'i*4 + 2', 'i8', null, true) }}};
}
return 1;
@@ -1283,12 +1441,12 @@ var LibrarySDL = {
IMG_Load_RW: function(rwopsID, freeSrc) {
try {
// stb_image integration support
- var cleanup = function() {
+ function cleanup() {
if (rwops && freeSrc) _SDL_FreeRW(rwopsID);
};
function addCleanup(func) {
var old = cleanup;
- cleanup = function() {
+ cleanup = function added_cleanup() {
old();
func();
}
@@ -1451,7 +1609,7 @@ var LibrarySDL = {
} else if (SDL.audio.channels != 1 && SDL.audio.channels != 2) { // Unsure what SDL audio spec supports. Web Audio spec supports up to 32 channels.
console.log('Warning: Using untested number of audio channels ' + SDL.audio.channels);
}
- if (SDL.audio.samples < 1024 || SDL.audio.samples > 524288 /* arbitrary cap */) {
+ if (SDL.audio.samples < 128 || SDL.audio.samples > 524288 /* arbitrary cap */) {
throw 'Unsupported audio callback buffer size ' + SDL.audio.samples + '!';
} else if ((SDL.audio.samples & (SDL.audio.samples-1)) != 0) {
throw 'Audio callback buffer size ' + SDL.audio.samples + ' must be a power-of-two!';
@@ -1462,8 +1620,12 @@ var LibrarySDL = {
SDL.audio.bufferSize = totalSamples*SDL.audio.bytesPerSample;
SDL.audio.buffer = _malloc(SDL.audio.bufferSize);
+ // To account for jittering in frametimes, always have multiple audio buffers queued up for the audio output device.
+ // This helps that we won't starve that easily if a frame takes long to complete.
+ SDL.audio.numSimultaneouslyQueuedBuffers = Module['SDL_numSimultaneouslyQueuedBuffers'] || 3;
+
// Create a callback function that will be routinely called to ask more audio data from the user application.
- SDL.audio.caller = function() {
+ SDL.audio.caller = function SDL_audio_caller() {
if (!SDL.audio) {
return;
}
@@ -1477,7 +1639,8 @@ var LibrarySDL = {
SDL.audio.audioOutput['mozSetup'](SDL.audio.channels, SDL.audio.freq); // use string attributes on mozOutput for closure compiler
SDL.audio.mozBuffer = new Float32Array(totalSamples);
SDL.audio.nextPlayTime = 0;
- SDL.audio.pushAudio = function(ptr, size) {
+ SDL.audio.pushAudio = function SDL_audio_pushAudio(ptr, size) {
+ --SDL.audio.numAudioTimersPending;
var mozBuffer = SDL.audio.mozBuffer;
// The input audio data for SDL audio is either 8-bit or 16-bit interleaved across channels, output for Mozilla Audio Data API
// needs to be Float32 interleaved, so perform a sample conversion.
@@ -1496,14 +1659,22 @@ var LibrarySDL = {
// Compute when the next audio callback should be called.
var curtime = Date.now() / 1000.0 - SDL.audio.startTime;
+#if ASSERTIONS
if (curtime > SDL.audio.nextPlayTime && SDL.audio.nextPlayTime != 0) {
console.log('warning: Audio callback had starved sending audio by ' + (curtime - SDL.audio.nextPlayTime) + ' seconds.');
}
+#endif
var playtime = Math.max(curtime, SDL.audio.nextPlayTime);
var buffer_duration = SDL.audio.samples / SDL.audio.freq;
SDL.audio.nextPlayTime = playtime + buffer_duration;
- // Schedule the next audio callback call.
+ // Schedule the next audio callback call to occur when the current one finishes.
SDL.audio.timer = Browser.safeSetTimeout(SDL.audio.caller, 1000.0 * (playtime-curtime));
+ ++SDL.audio.numAudioTimersPending;
+ // And also schedule extra buffers _now_ if we have too few in queue.
+ if (SDL.audio.numAudioTimersPending < SDL.audio.numSimultaneouslyQueuedBuffers) {
+ ++SDL.audio.numAudioTimersPending;
+ Browser.safeSetTimeout(SDL.audio.caller, 1.0);
+ }
}
} else {
// Initialize Web Audio API if we haven't done so yet. Note: Only initialize Web Audio context ever once on the web page,
@@ -1566,9 +1737,11 @@ var LibrarySDL = {
// Schedule the generated sample buffer to be played out at the correct time right after the previously scheduled
// sample buffer has finished.
var curtime = SDL.audioContext['currentTime'];
-// if (curtime > SDL.audio.nextPlayTime && SDL.audio.nextPlayTime != 0) {
-// console.log('warning: Audio callback had starved sending audio by ' + (curtime - SDL.audio.nextPlayTime) + ' seconds.');
-// }
+#if ASSERTIONS
+ if (curtime > SDL.audio.nextPlayTime && SDL.audio.nextPlayTime != 0) {
+ console.log('warning: Audio callback had starved sending audio by ' + (curtime - SDL.audio.nextPlayTime) + ' seconds.');
+ }
+#endif
var playtime = Math.max(curtime, SDL.audio.nextPlayTime);
SDL.audio.soundSource[SDL.audio.nextSoundSource]['start'](playtime);
var buffer_duration = sizeSamplesPerChannel / SDL.audio.freq;
@@ -1583,8 +1756,8 @@ var LibrarySDL = {
++SDL.audio.numAudioTimersPending;
}
- // If we are risking starving, immediately queue an extra second buffer.
- if (secsUntilNextCall <= buffer_duration && SDL.audio.numAudioTimersPending <= 1) {
+ // If we are risking starving, immediately queue extra buffers.
+ if (secsUntilNextCall <= buffer_duration && SDL.audio.numAudioTimersPending < SDL.audio.numSimultaneouslyQueuedBuffers) {
++SDL.audio.numAudioTimersPending;
Browser.safeSetTimeout(SDL.audio.caller, 1.0);
}
@@ -1844,7 +2017,7 @@ var LibrarySDL = {
audio.frequency = info.audio.frequency;
// TODO: handle N loops. Behavior matches Mix_PlayMusic
audio.loop = loops != 0;
- audio['onended'] = function() { // TODO: cache these
+ audio['onended'] = function SDL_audio_onended() { // TODO: cache these
channelInfo.audio = null;
if (SDL.channelFinished) {
Runtime.getFuncWrapper(SDL.channelFinished, 'vi')(channel);
@@ -1871,7 +2044,7 @@ var LibrarySDL = {
source.loop = false;
source.buffer = context.createBuffer(numChannels, 1, audio.frequency);
var jsNode = context.createJavaScriptNode(2048, numChannels, numChannels);
- jsNode.onaudioprocess = function(event) {
+ jsNode.onaudioprocess = function jsNode_onaudioprocess(event) {
var buffers = new Array(numChannels);
for (var i = 0; i < numChannels; ++i) {
buffers[i] = event.outputBuffer.getChannelData(i);
@@ -2357,37 +2530,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
@@ -2436,7 +2675,7 @@ var LibrarySDL = {
SDL_WaitThread: function() { throw 'SDL_WaitThread' },
SDL_GetThreadID: function() { throw 'SDL_GetThreadID' },
- SDL_ThreadID: function() { throw 'SDL_ThreadID' },
+ SDL_ThreadID: function() { return 0; },
SDL_AllocRW: function() { throw 'SDL_AllocRW: TODO' },
SDL_CondBroadcast: function() { throw 'SDL_CondBroadcast: TODO' },
SDL_CondWaitTimeout: function() { throw 'SDL_CondWaitTimeout: TODO' },
diff --git a/src/library_sockfs.js b/src/library_sockfs.js
index af29d11b..bc3aa997 100644
--- a/src/library_sockfs.js
+++ b/src/library_sockfs.js
@@ -138,7 +138,9 @@ mergeInto(LibraryManager.library, {
console.log('connect: ' + url);
#endif
// the node ws library API is slightly different than the browser's
- var opts = ENVIRONMENT_IS_NODE ? {} : ['binary'];
+ var opts = ENVIRONMENT_IS_NODE ? {headers: {'websocket-protocol': ['binary']}} : ['binary'];
+ // If node we use the ws library.
+ var WebSocket = ENVIRONMENT_IS_NODE ? require('ws') : window['WebSocket'];
ws = new WebSocket(url, opts);
ws.binaryType = 'arraybuffer';
} catch (e) {
@@ -208,7 +210,7 @@ mergeInto(LibraryManager.library, {
}
};
- var handleMessage = function(data) {
+ function handleMessage(data) {
assert(typeof data !== 'string' && data.byteLength !== undefined); // must receive an ArrayBuffer
data = new Uint8Array(data); // make a typed array view on the array buffer
@@ -247,7 +249,7 @@ mergeInto(LibraryManager.library, {
});
} else {
peer.socket.onopen = handleOpen;
- peer.socket.onmessage = function(event) {
+ peer.socket.onmessage = function peer_socket_onmessage(event) {
handleMessage(event.data);
};
}
@@ -573,4 +575,4 @@ mergeInto(LibraryManager.library, {
}
}
}
-}); \ No newline at end of file
+});
diff --git a/src/modules.js b/src/modules.js
index 854575e0..79f494c0 100644
--- a/src/modules.js
+++ b/src/modules.js
@@ -18,8 +18,9 @@ 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
+ MATHOP_IGNORABLES: set('exact', 'nnan', 'ninf', 'nsz', 'arcp', 'fast'),
};
LLVM.GLOBAL_MODIFIERS = set(keys(LLVM.LINKAGES).concat(['constant', 'global', 'hidden']));
@@ -253,13 +254,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 +289,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;
@@ -331,7 +342,7 @@ var Functions = {
// Resolve multi-level aliases all the way down
while (1) {
var varData = Variables.globals[table[i]];
- if (!(varData && varData.resolvedAlias && varData.resolvedAlias.indexOf('FUNCTION_TABLE_OFFSET') < 0)) break;
+ if (!(varData && varData.resolvedAlias && !/(FUNCTION_TABLE_OFFSET|F_BASE_)/.test(varData.resolvedAlias))) break;
table[i] = table[+varData.resolvedAlias || eval(varData.resolvedAlias)]; // might need to eval to turn (6) into 6
}
// Resolve library aliases
@@ -350,17 +361,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');
@@ -474,6 +483,10 @@ var PassManager = {
print('\n//FORWARDED_DATA:' + JSON.stringify({
Functions: { tables: Functions.tables }
}));
+ } else if (phase == 'glue') {
+ print('\n//FORWARDED_DATA:' + JSON.stringify({
+ Functions: Functions
+ }));
}
},
load: function(json) {
diff --git a/src/parseTools.js b/src/parseTools.js
index dae386f1..08cf9b60 100644
--- a/src/parseTools.js
+++ b/src/parseTools.js
@@ -157,6 +157,10 @@ function isStructType(type) {
return type[0] == '%';
}
+function isVectorType(type) {
+ return type[type.length-1] === '>';
+}
+
function isStructuralType(type) {
return /^{ ?[^}]* ?}$/.test(type); // { i32, i8 } etc. - anonymous struct types
}
@@ -215,8 +219,22 @@ function isIdenticallyImplemented(type1, type2) {
}
function isIllegalType(type) {
- var bits = getBits(type);
- return bits > 0 && (bits >= 64 || !isPowerOfTwo(bits));
+ switch (type) {
+ case 'i1':
+ case 'i8':
+ case 'i16':
+ case 'i32':
+ case 'float':
+ case 'double':
+ case 'rawJS':
+ case '<2 x float>':
+ case '<4 x float>':
+ case '<2 x i32>':
+ case '<4 x i32>':
+ case 'void': return false;
+ }
+ if (!type || type[type.length-1] === '*') return false;
+ return true;
}
function isVoidType(type) {
@@ -287,6 +305,9 @@ function getReturnType(type) {
if (pointingLevels(type) > 1) return '*'; // the type of a call can be either the return value, or the entire function. ** or more means it is a return value
var lastOpen = type.lastIndexOf('(');
if (lastOpen > 0) {
+ // handle things like void (i32)* (i32, void (i32)*)*
+ var closeStar = type.indexOf(')*');
+ if (closeStar > 0 && closeStar < type.length-2) lastOpen = closeStar+3;
return type.substr(0, lastOpen-1);
}
return type;
@@ -328,28 +349,29 @@ function getVectorSize(type) {
return parseInt(type.substring(1, type.indexOf(' ')));
}
-function getVectorBaseType(type) {
+function getVectorNativeType(type) {
Types.usesSIMD = true;
switch (type) {
case '<2 x float>':
case '<4 x float>': return 'float';
case '<2 x i32>':
- case '<4 x i32>': return 'uint';
+ case '<4 x i32>': return 'i32';
default: throw 'unknown vector type ' + type;
}
}
-function getVectorNativeType(type) {
- Types.usesSIMD = true;
+function getSIMDName(type) {
switch (type) {
- case '<2 x float>':
- case '<4 x float>': return 'float';
- case '<2 x i32>':
- case '<4 x i32>': return 'i32';
- default: throw 'unknown vector type ' + type;
+ case 'i32': return 'uint';
+ case 'float': return 'float';
+ default: throw 'getSIMDName ' + type;
}
}
+function getVectorBaseType(type) {
+ return getSIMDName(getVectorNativeType(type));
+}
+
function addIdent(token) {
token.ident = token.text;
return token;
@@ -465,26 +487,13 @@ function parseParamTokens(params) {
Types.needAnalysis[ret[ret.length-1].type] = 0;
anonymousIndex ++;
}
- } else if (segment[1].text in PARSABLE_LLVM_FUNCTIONS) {
- ret.push(parseLLVMFunctionCall(segment));
- } else if (segment[1].text === 'blockaddress') {
- ret.push(parseBlockAddress(segment));
- } else if (segment[1].type && segment[1].type == '{') {
- ret.push(parseLLVMSegment(segment));
} else {
if (segment[2] && segment[2].text == 'to') { // part of bitcast params
segment = segment.slice(0, 2);
}
- while (segment.length > 2) {
- segment[0].text += segment[1].text;
- segment.splice(1, 1); // TODO: merge tokens nicely
- }
- ret.push({
- intertype: 'value',
- type: segment[0].text,
- ident: toNiceIdent(parseNumerical(segment[1].text, segment[0].text))
- });
- Types.needAnalysis[removeAllPointing(ret[ret.length-1].type)] = 0;
+ var parsed = parseLLVMSegment(segment);
+ if (parsed.intertype === 'value' && !isIllegalType(parsed.type)) parsed.ident = parseNumerical(parsed.ident, parsed.type);
+ ret.push(parsed);
}
ret[ret.length-1].byVal = byVal;
}
@@ -558,25 +567,6 @@ function sortGlobals(globals) {
});
}
-function finalizeParam(param) {
- if (param.intertype in PARSABLE_LLVM_FUNCTIONS) {
- return finalizeLLVMFunctionCall(param);
- } else if (param.intertype === 'blockaddress') {
- return finalizeBlockAddress(param);
- } else if (param.intertype === 'jsvalue') {
- return param.ident;
- } else {
- if (param.type == 'i64' && USE_TYPED_ARRAYS == 2) {
- return parseI64Constant(param.ident);
- }
- var ret = toNiceIdent(param.ident);
- if (ret in Variables.globals) {
- ret = makeGlobalUse(ret);
- }
- return ret;
- }
-}
-
// Segment ==> Parameter
function parseLLVMSegment(segment) {
var type;
@@ -639,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);
@@ -798,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,
@@ -991,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 });
}
@@ -1009,11 +1007,9 @@ function getOldLabel(label) {
}
function calcAllocatedSize(type) {
- if (pointingLevels(type) == 0 && isStructType(type)) {
- return Types.types[type].flatSize; // makeEmptyStruct(item.allocatedType).length;
- } else {
- return Runtime.getNativeTypeSize(type); // We can really get away with '1', though, at least on the stack...
- }
+ var ret = Runtime.getNativeTypeSize(type);
+ if (ret) return ret;
+ return Types.types[type].flatSize; // known type
}
// Generates the type signature for a structure, for each byte, the type that is there.
@@ -1173,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';
}
@@ -1219,7 +1220,11 @@ function asmCoercion(value, type, signedness) {
value = '(' + value + ')|0';
}
}
- return '(+(' + value + '))';
+ if (PRECISE_F32 && type === 'float') {
+ return 'Math_fround(' + value + ')';
+ } else {
+ return '(+(' + value + '))';
+ }
}
} else {
return '((' + value + ')|0)';
@@ -1809,7 +1814,7 @@ function makeGetSlabs(ptr, type, allowMultiple, unsigned) {
switch(type) {
case 'i1': case 'i8': return [unsigned ? 'HEAPU8' : 'HEAP8']; break;
case 'i16': return [unsigned ? 'HEAPU16' : 'HEAP16']; break;
- case '<4 x i32>': case 'uint':
+ case '<4 x i32>':
case 'i32': case 'i64': return [unsigned ? 'HEAPU32' : 'HEAP32']; break;
case 'double': {
if (TARGET_LE32) return ['HEAPF64']; // in le32, we do have the ability to assume 64-bit alignment
@@ -2002,6 +2007,8 @@ function finalizeLLVMParameter(param, noIndexizeFunctions) {
} else if (param.ident == 'zeroinitializer') {
if (isStructType(param.type)) {
return makeLLVMStruct(zeros(Types.types[param.type].fields.length));
+ } else if (isVectorType(param.type)) {
+ return ensureVector(0, getVectorBaseType(param.type));
} else {
return '0';
}
@@ -2024,7 +2031,7 @@ function finalizeLLVMParameter(param, noIndexizeFunctions) {
} else if (param.intertype == 'mathop') {
return processMathop(param);
} else if (param.intertype === 'vector') {
- return 'float32x4(' + param.idents.join(',') + ')';
+ return getVectorBaseType(param.type) + '32x4(' + param.idents.join(',') + ')';
} else {
throw 'invalid llvm parameter: ' + param.intertype;
}
@@ -2051,7 +2058,7 @@ function makeSignOp(value, type, op, force, ignore) {
if (isPointerType(type)) type = 'i32'; // Pointers are treated as 32-bit ints
if (!value) return value;
var bits, full;
- if (type in Runtime.INT_TYPES) {
+ if (type[0] === 'i') {
bits = parseInt(type.substr(1));
full = op + 'Sign(' + value + ', ' + bits + ', ' + Math.floor(ignore || correctSpecificSign()) + ')';
// Always sign/unsign constants at compile time, regardless of CHECK/CORRECT
@@ -2060,7 +2067,7 @@ function makeSignOp(value, type, op, force, ignore) {
}
}
if ((ignore || !correctSigns()) && !CHECK_SIGNS && !force) return value;
- if (type in Runtime.INT_TYPES) {
+ if (type[0] === 'i') {
// this is an integer, but not a number (or we would have already handled it)
// shortcuts
if (!CHECK_SIGNS || ignore) {
@@ -2133,14 +2140,14 @@ function makeRounding(value, bits, signed, floatConversion) {
}
}
-function makeIsNaN(value) {
- if (ASM_JS) return makeInlineCalculation('((VALUE) != (VALUE))', value, 'tempDouble');
+function makeIsNaN(value, type) {
+ if (ASM_JS) return makeInlineCalculation('((VALUE) != (VALUE))', value, type === 'float' ? 'tempFloat' : 'tempDouble');
return 'isNaN(' + value + ')';
}
function makeFloat(value, type) {
- if (TO_FLOAT32 && type == 'float') {
- return 'Math_toFloat32(' + value + ')';
+ if (PRECISE_F32 && type == 'float') {
+ return 'Math_fround(' + value + ')';
}
return value;
}
@@ -2257,8 +2264,8 @@ function processMathop(item) {
case 'lshr': {
throw 'shifts should have been legalized!';
}
- case 'uitofp': case 'sitofp': return RuntimeGenerator.makeBigInt(low1, high1, op[0] == 'u');
- case 'fptoui': case 'fptosi': return finish(splitI64(idents[0], true));
+ case 'uitofp': case 'sitofp': return makeFloat(RuntimeGenerator.makeBigInt(low1, high1, op[0] == 'u'), item.type);
+ case 'fptoui': case 'fptosi': return finish(splitI64(asmCoercion(idents[0], 'double'), true)); // coerce to double before conversion to i64
case 'icmp': {
switch (variant) {
case 'uge': return '((' + high1 + '>>>0) >= (' + high2 + '>>>0)) & ((((' + high1 + '>>>0) > (' + high2 + '>>>0)) | ' +
@@ -2287,7 +2294,7 @@ function processMathop(item) {
case 'trunc': {
return '((' + idents[0] + '[0]) & ' + (Math.pow(2, bitsLeft)-1) + ')';
}
- case 'select': return idents[0] + ' ? ' + makeCopyI64(idents[1]) + ' : ' + makeCopyI64(idents[2]);
+ case 'select': return '(' + idents[0] + ' ? ' + makeCopyI64(idents[1]) + ' : ' + makeCopyI64(idents[2]) + ')';;
case 'ptrtoint': return makeI64(idents[0], 0);
case 'inttoptr': {
var m = /\(?\[(\d+),\d+\]\)?/.exec(idents[0]);
@@ -2384,6 +2391,9 @@ function processMathop(item) {
return 'SIMD.uint32x4BitsToFloat32x4(' + idents[0] + ')';
}
}
+ case 'and': return 'SIMD.and(' + idents[0] + ',' + idents[1] + ')';
+ case 'or': return 'SIMD.or(' + idents[0] + ',' + idents[1] + ')';
+ case 'xor': return 'SIMD.xor(' + idents[0] + ',' + idents[1] + ')';
default: throw 'vector op todo: ' + dump(item);
}
}
@@ -2439,12 +2449,17 @@ function processMathop(item) {
case 'fdiv': return makeFloat(getFastValue(idents[0], '/', idents[1], item.type), item.type);
case 'fmul': return makeFloat(getFastValue(idents[0], '*', idents[1], item.type), item.type);
case 'frem': return makeFloat(getFastValue(idents[0], '%', idents[1], item.type), item.type);
- case 'uitofp': case 'sitofp': return asmCoercion(idents[0], 'double', op[0]);
+ case 'uitofp': case 'sitofp': return asmCoercion(idents[0], item.type, op[0]);
case 'fptoui': case 'fptosi': return makeRounding(idents[0], bitsLeft, op === 'fptosi', true);
// TODO: We sometimes generate false instead of 0, etc., in the *cmps. It seemed slightly faster before, but worth rechecking
// Note that with typed arrays, these become 0 when written. So that is a potential difference with non-typed array runs.
case 'icmp': {
+ // unsigned coercions can be (X&Y), which is not a valid asm coercion for comparisons
+ if (ASM_JS && variant[0] === 'u') {
+ if (idents[0].indexOf('>>>') < 0) idents[0] = '((' + idents[0] + ')>>>0)';
+ if (idents[1].indexOf('>>>') < 0) idents[1] = '((' + idents[1] + ')>>>0)';
+ }
switch (variant) {
case 'uge': case 'sge': return idents[0] + '>=' + idents[1];
case 'ule': case 'sle': return idents[0] + '<=' + idents[1];
@@ -2471,8 +2486,8 @@ function processMathop(item) {
case 'ult': case 'olt': return idents[0] + '<' + idents[1];
case 'une': case 'one': return idents[0] + '!=' + idents[1];
case 'ueq': case 'oeq': return idents[0] + '==' + idents[1];
- case 'ord': return '!' + makeIsNaN(idents[0]) + '&!' + makeIsNaN(idents[1]);
- case 'uno': return makeIsNaN(idents[0]) + '|' + makeIsNaN(idents[1]);
+ case 'ord': return '!' + makeIsNaN(idents[0], paramTypes[0]) + '&!' + makeIsNaN(idents[1], paramTypes[0]);
+ case 'uno': return makeIsNaN(idents[0], paramTypes[0]) + '|' + makeIsNaN(idents[1], paramTypes[0]);
case 'true': return '1';
default: throw 'Unknown fcmp variant: ' + variant;
}
@@ -2486,9 +2501,16 @@ function processMathop(item) {
}
// otherwise, fall through
}
- case 'fpext': case 'sext': return idents[0];
- case 'fptrunc': return idents[0];
- case 'select': return idents[0] + '?' + asmEnsureFloat(idents[1], item.type) + ':' + asmEnsureFloat(idents[2], item.type);
+ case 'sext': return idents[0];
+ case 'fpext': {
+ if (PRECISE_F32) return '+(' + idents[0] + ')';
+ return idents[0];
+ }
+ case 'fptrunc': {
+ if (PRECISE_F32) return 'Math_fround(' + idents[0] + ')';
+ return idents[0];
+ }
+ case 'select': return '(' + idents[0] + '?' + asmEnsureFloat(idents[1], item.type) + ':' + asmEnsureFloat(idents[2], item.type) + ')';
case 'ptrtoint': case 'inttoptr': {
var ret = '';
if (QUANTUM_SIZE == 1) {
@@ -2678,3 +2700,14 @@ function ensureVector(ident, base) {
return ident == 0 ? base + '32x4.zero()' : ident;
}
+function ensureValidFFIType(type) {
+ return type === 'float' ? 'double' : type; // ffi does not tolerate float XXX
+}
+
+// FFI return values must arrive as doubles, and we can force them to floats afterwards
+function asmFFICoercion(value, type) {
+ value = asmCoercion(value, ensureValidFFIType(type));
+ if (PRECISE_F32 && type === 'float') value = asmCoercion(value, 'float');
+ return value;
+}
+
diff --git a/src/preamble.js b/src/preamble.js
index 2812d033..710b7c52 100644
--- a/src/preamble.js
+++ b/src/preamble.js
@@ -246,7 +246,7 @@ var EXITSTATUS = 0;
var undef = 0;
// tempInt is used for 32-bit signed values or smaller. tempBigInt is used
// for 32-bit unsigned values or more than 32 bits. TODO: audit all uses of tempInt
-var tempValue, tempInt, tempBigInt, tempInt2, tempBigInt2, tempPair, tempBigIntI, tempBigIntR, tempBigIntS, tempBigIntP, tempBigIntD;
+var tempValue, tempInt, tempBigInt, tempInt2, tempBigInt2, tempPair, tempBigIntI, tempBigIntR, tempBigIntS, tempBigIntP, tempBigIntD, tempDouble, tempFloat;
#if USE_TYPED_ARRAYS == 2
var tempI64, tempI64b;
var tempRet0, tempRet1, tempRet2, tempRet3, tempRet4, tempRet5, tempRet6, tempRet7, tempRet8, tempRet9;
@@ -646,6 +646,10 @@ function demangle(func) {
if (func[0] !== '_') return func;
if (func[1] !== '_') return func; // C function
if (func[2] !== 'Z') return func;
+ switch (func[3]) {
+ case 'n': return 'operator new()';
+ case 'd': return 'operator delete()';
+ }
var i = 3;
// params, etc.
var basicTypes = {
@@ -678,7 +682,7 @@ function demangle(func) {
var subs = [];
function parseNested() {
i++;
- if (func[i] === 'K') i++;
+ if (func[i] === 'K') i++; // ignore const
var parts = [];
while (func[i] !== 'E') {
if (func[i] === 'S') { // substitution
@@ -689,6 +693,11 @@ function demangle(func) {
i = next+1;
continue;
}
+ if (func[i] === 'C') { // constructor
+ parts.push(parts[parts.length-1]);
+ i += 2;
+ continue;
+ }
var size = parseInt(func.substr(i));
var pre = size.toString().length;
if (!size || !pre) { i--; break; } // counter i++ below us
@@ -700,6 +709,7 @@ function demangle(func) {
i++; // skip E
return parts;
}
+ var first = true;
function parse(rawList, limit, allowVoid) { // main parser
limit = limit || Infinity;
var ret = '', list = [];
@@ -707,21 +717,22 @@ function demangle(func) {
return '(' + list.join(', ') + ')';
}
var name;
- if (func[i] !== 'N') {
+ if (func[i] === 'N') {
+ // namespaced N-E
+ name = parseNested().join('::');
+ limit--;
+ if (limit === 0) return rawList ? [name] : name;
+ } else {
// not namespaced
- if (func[i] === 'K') i++;
+ if (func[i] === 'K' || (first && func[i] === 'L')) i++; // ignore const and first 'L'
var size = parseInt(func.substr(i));
if (size) {
var pre = size.toString().length;
name = func.substr(i + pre, size);
i += pre + size;
}
- } else {
- // namespaced N-E
- name = parseNested().join('::');
- limit--;
- if (limit === 0) return rawList ? [name] : name;
}
+ first = false;
if (func[i] === 'I') {
i++;
var iList = parse(true);
@@ -1060,7 +1071,7 @@ Module['writeAsciiToMemory'] = writeAsciiToMemory;
{{{ reSign }}}
#if PRECISE_I32_MUL
-if (!Math['imul']) Math['imul'] = function(a, b) {
+if (!Math['imul']) Math['imul'] = function imul(a, b) {
var ah = a >>> 16;
var al = a & 0xffff;
var bh = b >>> 16;
@@ -1068,17 +1079,22 @@ if (!Math['imul']) Math['imul'] = function(a, b) {
return (al*bl + ((ah*bl + al*bh) << 16))|0;
};
#else
-Math['imul'] = function(a, b) {
+Math['imul'] = function imul(a, b) {
return (a*b)|0; // fast but imprecise
};
#endif
Math.imul = Math['imul'];
-#if TO_FLOAT32
-if (!Math['toFloat32']) Math['toFloat32'] = function(x) {
- return x;
-};
-Math.toFloat32 = Math['toFloat32'];
+#if PRECISE_F32
+#if PRECISE_F32 == 1
+if (!Math['fround']) {
+ var froundBuffer = new Float32Array(1);
+ Math['fround'] = function(x) { froundBuffer[0] = x; return froundBuffer[0] };
+}
+#else // 2
+if (!Math['fround']) Math['fround'] = function(x) { return x };
+#endif
+Math.fround = Math['fround'];
#endif
var Math_abs = Math.abs;
@@ -1096,7 +1112,7 @@ var Math_ceil = Math.ceil;
var Math_floor = Math.floor;
var Math_pow = Math.pow;
var Math_imul = Math.imul;
-var Math_toFloat32 = Math.toFloat32;
+var Math_fround = Math.fround;
var Math_min = Math.min;
// A counter of dependencies for calling run(). If we need to
@@ -1107,19 +1123,21 @@ var Math_min = Math.min;
// it happens right before run - run will be postponed until
// the dependencies are met.
var runDependencies = 0;
-var runDependencyTracking = {};
var runDependencyWatcher = null;
var dependenciesFulfilled = null; // overridden to take different actions when all run dependencies are fulfilled
+#if ASSERTIONS
+var runDependencyTracking = {};
+#endif
function addRunDependency(id) {
runDependencies++;
if (Module['monitorRunDependencies']) {
Module['monitorRunDependencies'](runDependencies);
}
+#if ASSERTIONS
if (id) {
assert(!runDependencyTracking[id]);
runDependencyTracking[id] = 1;
-#if ASSERTIONS
if (runDependencyWatcher === null && typeof setInterval !== 'undefined') {
// Check for missing dependencies every few seconds
runDependencyWatcher = setInterval(function() {
@@ -1136,10 +1154,10 @@ function addRunDependency(id) {
}
}, 10000);
}
-#endif
} else {
Module.printErr('warning: run dependency added without ID');
}
+#endif
}
Module['addRunDependency'] = addRunDependency;
function removeRunDependency(id) {
@@ -1147,12 +1165,14 @@ function removeRunDependency(id) {
if (Module['monitorRunDependencies']) {
Module['monitorRunDependencies'](runDependencies);
}
+#if ASSERTIONS
if (id) {
assert(runDependencyTracking[id]);
delete runDependencyTracking[id];
} else {
Module.printErr('warning: run dependency removed without ID');
}
+#endif
if (runDependencies == 0) {
if (runDependencyWatcher !== null) {
clearInterval(runDependencyWatcher);
diff --git a/src/proxyClient.js b/src/proxyClient.js
index 38ea5771..8f4ad7a6 100644
--- a/src/proxyClient.js
+++ b/src/proxyClient.js
@@ -5,7 +5,7 @@ Module.ctx = Module.canvas.getContext('2d');
var worker = new Worker('{{{ filename }}}.js');
-worker.onmessage = function(event) {
+worker.onmessage = function worker_onmessage(event) {
var data = event.data;
switch (data.target) {
case 'stdout': {
diff --git a/src/proxyWorker.js b/src/proxyWorker.js
index 29b2528d..5d34b900 100644
--- a/src/proxyWorker.js
+++ b/src/proxyWorker.js
@@ -2,12 +2,12 @@
function EventListener() {
this.listeners = {};
- this.addEventListener = function(event, func) {
+ this.addEventListener = function addEventListener(event, func) {
if (!this.listeners[event]) this.listeners[event] = [];
this.listeners[event].push(func);
};
- this.fireEvent = function(event) {
+ this.fireEvent = function fireEvent(event) {
event.preventDefault = function(){};
if (event.type in this.listeners) {
@@ -22,17 +22,17 @@ var window = this;
var windowExtra = new EventListener();
for (var x in windowExtra) window[x] = windowExtra[x];
-window.close = function() {
+window.close = function window_close() {
postMessage({ target: 'window', method: 'close' });
};
var document = new EventListener();
-document.createElement = function(what) {
+document.createElement = function document_createElement(what) {
switch(what) {
case 'canvas': {
var canvas = new EventListener();
- canvas.ensureData = function() {
+ canvas.ensureData = function canvas_ensureData() {
if (!canvas.data || canvas.data.width !== canvas.width || canvas.data.height !== canvas.height) {
canvas.data = {
width: canvas.width,
@@ -42,7 +42,7 @@ document.createElement = function(what) {
postMessage({ target: 'canvas', op: 'resize', width: canvas.width, height: canvas.height });
}
};
- canvas.getContext = function(type) {
+ canvas.getContext = function canvas_getContext(type) {
assert(type == '2d');
return {
getImageData: function(x, y, w, h) {
@@ -63,7 +63,7 @@ document.createElement = function(what) {
};
};
canvas.boundingClientRect = {};
- canvas.getBoundingClientRect = function() {
+ canvas.getBoundingClientRect = function canvas_getBoundingClientRect() {
return {
width: canvas.boundingClientRect.width,
height: canvas.boundingClientRect.height,
@@ -89,10 +89,10 @@ Module.canvas = document.createElement('canvas');
Module.setStatus = function(){};
-Module.print = function(x) {
+Module.print = function Module_print(x) {
postMessage({ target: 'stdout', content: x });
};
-Module.printErr = function(x) {
+Module.printErr = function Module_printErr(x) {
postMessage({ target: 'stderr', content: x });
};
@@ -112,7 +112,7 @@ function messageResender() {
}
}
-onmessage = function(message) {
+onmessage = function onmessage(message) {
if (!calledMain) {
if (!messageBuffer) {
messageBuffer = [];
diff --git a/src/relooper/Relooper.cpp b/src/relooper/Relooper.cpp
index d79dca5a..07b36311 100644
--- a/src/relooper/Relooper.cpp
+++ b/src/relooper/Relooper.cpp
@@ -6,7 +6,12 @@
#include <list>
#include <stack>
+#if EMSCRIPTEN
#include "ministring.h"
+#else
+#include <string>
+typedef std::string ministring;
+#endif
template <class T, class U> bool contains(const T& container, const U& contained) {
return container.find(contained) != container.end();
@@ -541,6 +546,16 @@ void Relooper::Calculate(Block *Entry) {
for (BlockSet::iterator iter = Curr->BranchesIn.begin(); iter != Curr->BranchesIn.end(); iter++) {
Queue.insert(*iter);
}
+#if 0
+ // Add elements it leads to, if they are dead ends. There is no reason not to hoist dead ends
+ // into loops, as it can avoid multiple entries after the loop
+ for (BlockBranchMap::iterator iter = Curr->BranchesOut.begin(); iter != Curr->BranchesOut.end(); iter++) {
+ Block *Target = iter->first;
+ if (Target->BranchesIn.size() <= 1 && Target->BranchesOut.size() == 0) {
+ Queue.insert(Target);
+ }
+ }
+#endif
}
}
assert(InnerBlocks.size() > 0);
diff --git a/src/runtime.js b/src/runtime.js
index fa127fe7..dedaf5ea 100644
--- a/src/runtime.js
+++ b/src/runtime.js
@@ -82,8 +82,8 @@ var RuntimeGenerator = {
// Rounding is inevitable if the number is large. This is a particular problem for small negative numbers
// (-1 will be rounded!), so handle negatives separately and carefully
makeBigInt: function(low, high, unsigned) {
- var unsignedRet = '(' + asmCoercion(makeSignOp(low, 'i32', 'un', 1, 1), 'float') + '+(' + asmCoercion(makeSignOp(high, 'i32', 'un', 1, 1), 'float') + '*' + asmEnsureFloat(4294967296, 'float') + '))';
- var signedRet = '(' + asmCoercion(makeSignOp(low, 'i32', 'un', 1, 1), 'float') + '+(' + asmCoercion(makeSignOp(high, 'i32', 're', 1, 1), 'float') + '*' + asmEnsureFloat(4294967296, 'float') + '))';
+ var unsignedRet = '(' + asmCoercion(makeSignOp(low, 'i32', 'un', 1, 1), 'double') + '+(' + asmCoercion(makeSignOp(high, 'i32', 'un', 1, 1), 'double') + '*' + asmEnsureFloat(4294967296, 'double') + '))';
+ var signedRet = '(' + asmCoercion(makeSignOp(low, 'i32', 'un', 1, 1), 'double') + '+(' + asmCoercion(makeSignOp(high, 'i32', 're', 1, 1), 'double') + '*' + asmEnsureFloat(4294967296, 'double') + '))';
if (typeof unsigned === 'string') return '(' + unsigned + ' ? ' + unsignedRet + ' : ' + signedRet + ')';
return unsigned ? unsignedRet : signedRet;
}
@@ -145,7 +145,7 @@ var Runtime = {
//! @param type The type, by name.
getNativeTypeSize: function(type) {
#if QUANTUM_SIZE == 1
- return 1;
+ return 1;
#else
switch (type) {
case 'i1': case 'i8': return 1;
@@ -161,6 +161,8 @@ var Runtime = {
var bits = parseInt(type.substr(1));
assert(bits % 8 === 0);
return bits/8;
+ } else {
+ return 0;
}
}
}
@@ -224,9 +226,16 @@ var Runtime = {
// bN, large number field, like a [N x i8]
size = field.substr(1)|0;
alignSize = 1;
- } else {
- assert(field[0] === '<', field); // assumed to be a vector type, if none of the above
+ } else if (field[0] === '<') {
+ // vector type
size = alignSize = Types.types[field].flatSize; // fully aligned
+ } else if (field[0] === 'i') {
+ // illegal integer field, that could not be legalized because it is an internal structure field
+ // it is ok to have such fields, if we just use them as markers of field size and nothing more complex
+ size = alignSize = parseInt(field.substr(1))/8;
+ assert(size % 1 === 0, 'cannot handle non-byte-size field ' + field);
+ } else {
+ assert(false, 'invalid type for calculateStructAlignment');
}
if (type.packed) alignSize = 1;
type.alignSize = Math.max(type.alignSize, alignSize);
@@ -372,6 +381,18 @@ var Runtime = {
#endif
},
+ getAsmConst: function(code, numArgs) {
+ // code is a constant string on the heap, so we can cache these
+ if (!Runtime.asmConstCache) Runtime.asmConstCache = {};
+ var func = Runtime.asmConstCache[code];
+ if (func) return func;
+ var args = [];
+ for (var i = 0; i < numArgs; i++) {
+ args.push(String.fromCharCode(36) + i); // $0, $1 etc
+ }
+ return Runtime.asmConstCache[code] = eval('(function(' + args.join(',') + '){ ' + Pointer_stringify(code) + ' })'); // new Function does not allow upvars in node
+ },
+
warnOnce: function(text) {
if (!Runtime.warnOnce.shown) Runtime.warnOnce.shown = {};
if (!Runtime.warnOnce.shown[text]) {
@@ -385,7 +406,7 @@ var Runtime = {
getFuncWrapper: function(func, sig) {
assert(sig);
if (!Runtime.funcWrappers[func]) {
- Runtime.funcWrappers[func] = function() {
+ Runtime.funcWrappers[func] = function dynCall_wrapper() {
return Runtime.dynCall(sig, func, arguments);
};
}
@@ -443,7 +464,7 @@ var Runtime = {
buffer.length = 0;
return ret;
}
- this.processJSString = function(string) {
+ this.processJSString = function processJSString(string) {
string = unescape(encodeURIComponent(string));
var ret = [];
for (var i = 0; i < string.length; i++) {
diff --git a/src/settings.js b/src/settings.js
index d2b47dc8..64884eec 100644
--- a/src/settings.js
+++ b/src/settings.js
@@ -23,7 +23,8 @@ var QUANTUM_SIZE = 4; // This is the size of an individual field in a structure.
// Changing this from the default of 4 is deprecated.
var TARGET_X86 = 0; // For i386-pc-linux-gnu
-var TARGET_LE32 = 1; // For le32-unknown-nacl
+var TARGET_LE32 = 1; // For le32-unknown-nacl. 1 is normal, 2 is for the fastcomp llvm
+ // backend using pnacl abi simplification
var CORRECT_SIGNS = 1; // Whether we make sure to convert unsigned values to signed values.
// Decreases performance with additional runtime checks. Might not be
@@ -115,7 +116,13 @@ var PRECISE_I64_MATH = 1; // If enabled, i64 addition etc. is emulated - which i
var PRECISE_I32_MUL = 1; // If enabled, i32 multiplication is done with full precision, which means it is
// correct even if the value exceeds the JS double-integer limit of ~52 bits (otherwise,
// rounding will occur above that range).
-var TO_FLOAT32 = 0; // Use Math.toFloat32
+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.
+ // 2: Model C++ floats precisely using Math.fround if available in the JS engine, otherwise
+ // use an empty polyfill. This will have less of a speed penalty than using the full
+ // polyfill in cases where engine support is not present.
var CLOSURE_ANNOTATIONS = 0; // If set, the generated code will be annotated for the closure
// compiler. This potentially lets closure optimize the code better.
@@ -384,13 +391,16 @@ var FAKE_X86_FP80 = 1; // Replaces x86_fp80 with double. This loses precision. I
var GC_SUPPORT = 1; // Enables GC, see gc.h (this does not add overhead, so it is on by default)
-var WARN_ON_UNDEFINED_SYMBOLS = 0; // If set to 1, we will warn on any undefined symbols that
- // are not resolved by the library_*.js files. We by default
- // do not warn because (1) it is normal in large projects to
+var WARN_ON_UNDEFINED_SYMBOLS = 1; // If set to 1, we will warn on any undefined symbols that
+ // are not resolved by the library_*.js files. Note that
+ // it is common in large projects to
// not implement everything, when you know what is not
// going to actually be called (and don't want to mess with
- // the existing buildsystem), and (2) functions might be
- // implemented later on, say in --pre-js
+ // the existing buildsystem), and functions might be
+ // implemented later on, say in --pre-js, so you may
+ // want to build with -s WARN_ON_UNDEFINED_SYMBOLS=0 to
+ // disable the warnings if they annoy you.
+ // See also ERROR_ON_UNDEFINED_SYMBOLS
var ERROR_ON_UNDEFINED_SYMBOLS = 0; // If set to 1, we will give a compile-time error on any
// undefined symbols (see WARN_ON_UNDEFINED_SYMBOLS).
@@ -410,7 +420,8 @@ var HEADLESS = 0; // If 1, will include shim code that tries to 'fake' a browser
var BENCHMARK = 0; // If 1, will just time how long main() takes to execute, and not
// print out anything at all whatsoever. This is useful for benchmarking.
-var ASM_JS = 0; // If 1, generate code in asm.js format.
+var ASM_JS = 0; // If 1, generate code in asm.js format. If 2, emits the same code except
+ // for omitting 'use asm'
var PGO = 0; // Enables profile-guided optimization in the form of runtime checks for
// which functions are actually called. Emits a list during shutdown that you
@@ -421,6 +432,8 @@ var DEAD_FUNCTIONS = []; // Functions on this list are not converted to JS, and
// reducing code size.
// If a dead function is actually called, you will get a runtime
// error.
+ // This can affect both functions in compiled code, and system
+ // library functions (e.g., you can use this to kill printf).
// TODO: options to lazily load such functions
var EXPLICIT_ZEXT = 0; // If 1, generate an explicit conversion of zext i1 to i32, using ?:
diff --git a/src/shell.html b/src/shell.html
index ff5f6e35..53a4fffb 100644
--- a/src/shell.html
+++ b/src/shell.html
@@ -63,8 +63,11 @@
},
canvas: document.getElementById('canvas'),
setStatus: function(text) {
- if (Module.setStatus.interval) clearInterval(Module.setStatus.interval);
+ if (!Module.setStatus.last) Module.setStatus.last = { time: Date.now(), text: '' };
+ if (text === Module.setStatus.text) return;
var m = text.match(/([^(]+)\((\d+(\.\d+)?)\/(\d+)\)/);
+ var now = Date.now();
+ if (m && now - Date.now() < 30) return; // if this is a progress update, skip it if too soon
var statusElement = document.getElementById('status');
var progressElement = document.getElementById('progress');
if (m) {
@@ -87,6 +90,6 @@
};
Module.setStatus('Downloading...');
</script>
- <script async type='text/javascript'>{{{ SCRIPT_CODE }}}</script>
+ {{{ SCRIPT }}}
</body>
</html>
diff --git a/src/shell.js b/src/shell.js
index be23b3c1..b41fbb51 100644
--- a/src/shell.js
+++ b/src/shell.js
@@ -38,17 +38,17 @@ var ENVIRONMENT_IS_SHELL = !ENVIRONMENT_IS_WEB && !ENVIRONMENT_IS_NODE && !ENVIR
if (ENVIRONMENT_IS_NODE) {
// Expose functionality in the same simple way that the shells work
// Note that we pollute the global namespace here, otherwise we break in node
- Module['print'] = function(x) {
+ Module['print'] = function print(x) {
process['stdout'].write(x + '\n');
};
- Module['printErr'] = function(x) {
+ Module['printErr'] = function printErr(x) {
process['stderr'].write(x + '\n');
};
var nodeFS = require('fs');
var nodePath = require('path');
- Module['read'] = function(filename, binary) {
+ Module['read'] = function read(filename, binary) {
filename = nodePath['normalize'](filename);
var ret = nodeFS['readFileSync'](filename);
// The path is absolute if the normalized version is the same as the resolved.
@@ -60,9 +60,9 @@ if (ENVIRONMENT_IS_NODE) {
return ret;
};
- Module['readBinary'] = function(filename) { return Module['read'](filename, true) };
+ Module['readBinary'] = function readBinary(filename) { return Module['read'](filename, true) };
- Module['load'] = function(f) {
+ Module['load'] = function load(f) {
globalEval(read(f));
};
@@ -77,10 +77,10 @@ else if (ENVIRONMENT_IS_SHELL) {
if (typeof read != 'undefined') {
Module['read'] = read;
} else {
- Module['read'] = function() { throw 'no read() available (jsc?)' };
+ Module['read'] = function read() { throw 'no read() available (jsc?)' };
}
- Module['readBinary'] = function(f) {
+ Module['readBinary'] = function readBinary(f) {
return read(f, 'binary');
};
@@ -91,9 +91,11 @@ else if (ENVIRONMENT_IS_SHELL) {
}
this['{{{ EXPORT_NAME }}}'] = Module;
+
+ eval("if (typeof gc === 'function' && gc.toString().indexOf('[native code]') > 0) var gc = undefined"); // wipe out the SpiderMonkey shell 'gc' function, which can confuse closure (uses it as a minified name, and it is then initted to a non-falsey value unexpectedly)
}
else if (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) {
- Module['read'] = function(url) {
+ Module['read'] = function read(url) {
var xhr = new XMLHttpRequest();
xhr.open('GET', url, false);
xhr.send(null);
@@ -105,10 +107,10 @@ else if (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) {
}
if (typeof console !== 'undefined') {
- Module['print'] = function(x) {
+ Module['print'] = function print(x) {
console.log(x);
};
- Module['printErr'] = function(x) {
+ Module['printErr'] = function printErr(x) {
console.log(x);
};
} else {
@@ -136,7 +138,7 @@ function globalEval(x) {
eval.call(null, x);
}
if (!Module['load'] == 'undefined' && Module['read']) {
- Module['load'] = function(f) {
+ Module['load'] = function load(f) {
globalEval(Module['read'](f));
};
}
diff --git a/src/struct_info.json b/src/struct_info.json
index 5b4726e8..c136cc8b 100644
--- a/src/struct_info.json
+++ b/src/struct_info.json
@@ -169,7 +169,8 @@
{
"file": "libc/time.h",
"defines": [
- ["li", "CLOCKS_PER_SEC"]
+ ["li", "CLOCKS_PER_SEC"],
+ "CLOCK_REALTIME"
],
"structs": {
"timezone": [
@@ -290,7 +291,11 @@
"AI_CANONNAME",
"AI_PASSIVE",
"NI_NAMEREQD",
- "EAI_NONAME",
+ "EAI_NONAME",
+ "EAI_AGAIN",
+ "EAI_FAIL",
+ "EAI_MEMORY",
+ "EAI_SYSTEM",
"EAI_SOCKTYPE",
"EAI_BADFLAGS"
],
@@ -961,6 +966,21 @@
"x",
"y"
],
+ "SDL_JoyAxisEvent": [
+ "type",
+ "which",
+ "axis",
+ "padding1",
+ "padding2",
+ "value"
+ ],
+ "SDL_JoyButtonEvent": [
+ "type",
+ "which",
+ "button",
+ "state",
+ "padding1"
+ ],
"SDL_ResizeEvent": [
"type",
"w",
diff --git a/src/utility.js b/src/utility.js
index ac821a89..cd27b209 100644
--- a/src/utility.js
+++ b/src/utility.js
@@ -68,7 +68,7 @@ function warn(a, msg) {
a = false;
}
if (!a) {
- printErr('Warning: ' + msg);
+ printErr('warning: ' + msg);
}
}
@@ -81,7 +81,7 @@ function warnOnce(a, msg) {
if (!warnOnce.msgs) warnOnce.msgs = {};
if (msg in warnOnce.msgs) return;
warnOnce.msgs[msg] = true;
- printErr('Warning: ' + msg);
+ printErr('warning: ' + msg);
}
}
@@ -89,7 +89,7 @@ var abortExecution = false;
function error(msg) {
abortExecution = true;
- printErr('Error: ' + msg);
+ printErr('error: ' + msg);
}
function dedup(items, ident) {
@@ -222,7 +222,8 @@ function mergeInto(obj, other) {
}
function isNumber(x) {
- return x == parseFloat(x) || (typeof x == 'string' && x.match(/^-?\d+$/));
+ // XXX this does not handle 0xabc123 etc. We should likely also do x == parseInt(x) (which handles that), and remove hack |// handle 0x... as well|
+ return x == parseFloat(x) || (typeof x == 'string' && x.match(/^-?\d+$/)) || x === 'NaN';
}
function isArray(x) {