diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/analyzer.js | 112 | ||||
-rw-r--r-- | src/compiler.js | 11 | ||||
-rw-r--r-- | src/intertyper.js | 49 | ||||
-rw-r--r-- | src/jsifier.js | 12 | ||||
-rw-r--r-- | src/library.js | 8 | ||||
-rw-r--r-- | src/library_browser.js | 1 | ||||
-rw-r--r-- | src/library_fs.js | 34 | ||||
-rw-r--r-- | src/library_gl.js | 184 | ||||
-rw-r--r-- | src/library_glut.js | 37 | ||||
-rw-r--r-- | src/library_memfs.js | 1 | ||||
-rw-r--r-- | src/library_sdl.js | 2 | ||||
-rw-r--r-- | src/modules.js | 6 | ||||
-rw-r--r-- | src/parseTools.js | 21 | ||||
-rw-r--r-- | src/postamble.js | 3 | ||||
-rw-r--r-- | src/preamble.js | 149 |
15 files changed, 502 insertions, 128 deletions
diff --git a/src/analyzer.js b/src/analyzer.js index 17ad26ad..95fbccc7 100644 --- a/src/analyzer.js +++ b/src/analyzer.js @@ -188,7 +188,7 @@ function analyzer(data, sidePass) { if (USE_TYPED_ARRAYS == 2) { function getLegalVars(base, bits, allowLegal) { bits = bits || 32; // things like pointers are all i32, but show up as 0 bits from getBits - if (allowLegal && bits <= 32) return [{ ident: base + ('i' + bits in Runtime.INT_TYPES ? '' : '$0'), bits: bits }]; + if (allowLegal && bits <= 32) return [{ intertype: 'value', ident: base + ('i' + bits in Runtime.INT_TYPES ? '' : '$0'), bits: bits, type: 'i' + bits }]; if (isNumber(base)) return getLegalLiterals(base, bits); if (base[0] == '{') { warnOnce('seeing source of illegal data ' + base + ', likely an inline struct - assuming zeroinit'); @@ -198,7 +198,7 @@ function analyzer(data, sidePass) { var i = 0; if (base == 'zeroinitializer' || base == 'undef') base = 0; while (bits > 0) { - ret[i] = { ident: base ? base + '$' + i : '0', bits: Math.min(32, bits) }; + ret[i] = { intertype: 'value', ident: base ? base + '$' + i : '0', bits: Math.min(32, bits), type: 'i' + Math.min(32, bits) }; bits -= 32; i++; } @@ -209,7 +209,7 @@ function analyzer(data, sidePass) { var ret = new Array(Math.ceil(bits/32)); var i = 0; while (bits > 0) { - ret[i] = { ident: (parsed[i]|0).toString(), bits: Math.min(32, bits) }; // resign all values + ret[i] = { intertype: 'value', ident: (parsed[i]|0).toString(), bits: Math.min(32, bits), type: 'i' + Math.min(32, bits) }; // resign all values bits -= 32; i++; } @@ -225,7 +225,8 @@ function analyzer(data, sidePass) { return getLegalLiterals(value.ident, bits); } else if (value.intertype == 'structvalue') { return getLegalStructuralParts(value).map(function(part) { - return { ident: part.ident, bits: part.type.substr(1) }; + part.bits = part.type.substr(1); // can be some nested IR, like LLVM calls + return part; }); } else { return getLegalVars(value.ident, bits); @@ -550,11 +551,7 @@ function analyzer(data, sidePass) { return { intertype: 'phiparam', label: param.label, - value: { - intertype: 'value', - ident: values[k++][j].ident, - type: 'i' + element.bits, - } + value: values[k++][j] }; }) }); @@ -802,27 +799,65 @@ function analyzer(data, sidePass) { var whole = shifts >= 0 ? Math.floor(shifts/32) : Math.ceil(shifts/32); var fraction = Math.abs(shifts % 32); if (signed) { - var signedFill = '(' + makeSignOp(sourceElements[sourceElements.length-1].ident, 'i' + sourceElements[sourceElements.length-1].bits, 're', 1, 1) + ' < 0 ? -1 : 0)'; - var signedKeepAlive = { intertype: 'value', ident: sourceElements[sourceElements.length-1].ident, type: 'i32' }; - } - for (var j = 0; j < targetElements.length; j++) { - var result = { - intertype: 'value', - ident: (j + whole >= 0 && j + whole < sourceElements.length) ? sourceElements[j + whole].ident : (signed ? signedFill : '0'), - params: [(signed && j + whole > sourceElements.length) ? signedKeepAlive : null], + var signedFill = { + intertype: 'mathop', + op: 'select', + variant: 's', type: 'i32', + params: [{ + intertype: 'mathop', + op: 'icmp', + variant: 'slt', + type: 'i32', + params: [ + { intertype: 'value', ident: sourceElements[sourceElements.length-1].ident, type: 'i' + Math.min(sourceBits, 32) }, + { intertype: 'value', ident: '0', type: 'i32' } + ] + }, + { intertype: 'value', ident: '-1', type: 'i32' }, + { intertype: 'value', ident: '0', type: 'i32' }, + ] }; - if (j == 0 && sourceBits < 32) { - // zext sign correction - result.ident = makeSignOp(result.ident, 'i' + sourceBits, isUnsignedOp(value.op) ? 'un' : 're', 1, 1); - } - if (fraction != 0) { - var other = { + } + for (var j = 0; j < targetElements.length; j++) { + var inBounds = j + whole >= 0 && j + whole < sourceElements.length; + var result; + if (inBounds || !signed) { + result = { intertype: 'value', - ident: (j + sign + whole >= 0 && j + sign + whole < sourceElements.length) ? sourceElements[j + sign + whole].ident : (signed ? signedFill : '0'), - params: [(signed && j + sign + whole > sourceElements.length) ? signedKeepAlive : null], - type: 'i32', + ident: inBounds ? sourceElements[j + whole].ident : '0', + type: 'i' + Math.min(sourceBits, 32), }; + if (j == 0 && sourceBits < 32) { + // zext sign correction + var result2 = { + intertype: 'mathop', + op: isUnsignedOp(value.op) ? 'zext' : 'sext', + params: [result, { + intertype: 'type', + ident: 'i32', + type: 'i' + sourceBits + }], + type: 'i32' + }; + result = result2; + } + } else { + // out of bounds and signed + result = copy(signedFill); + } + if (fraction != 0) { + var other; + var otherInBounds = j + sign + whole >= 0 && j + sign + whole < sourceElements.length; + if (otherInBounds || !signed) { + other = { + intertype: 'value', + ident: otherInBounds ? sourceElements[j + sign + whole].ident : '0', + type: 'i32', + }; + } else { + other = copy(signedFill); + } other = { intertype: 'mathop', op: shiftOp, @@ -872,10 +907,17 @@ function analyzer(data, sidePass) { } if (targetBits <= 32) { // We are generating a normal legal type here - legalValue = { - intertype: 'value', - ident: targetElements[0].ident + (targetBits < 32 ? '&' + (Math.pow(2, targetBits)-1) : ''), - type: 'rawJS' + legalValue = { intertype: 'value', ident: targetElements[0].ident, type: 'i32' }; + if (targetBits < 32) { + legalValue = { + intertype: 'mathop', + op: 'and', + type: 'i32', + params: [ + legalValue, + { intertype: 'value', ident: (Math.pow(2, targetBits)-1).toString(), type: 'i32' } + ] + } }; legalValue.assignTo = item.assignTo; toAdd.push(legalValue); @@ -1117,7 +1159,7 @@ function analyzer(data, sidePass) { rawLinesIndex: i }; if (variable.origin === 'alloca') { - variable.allocatedNum = item.allocatedNum; + variable.allocatedNum = item.ident; } if (variable.origin === 'call') { variable.type = getReturnType(variable.type); @@ -1608,9 +1650,9 @@ function analyzer(data, sidePass) { var lines = func.labels[0].lines; for (var i = 0; i < lines.length; i++) { var item = lines[i]; - if (!item.assignTo || item.intertype != 'alloca' || !isNumber(item.allocatedNum)) break; + if (!item.assignTo || item.intertype != 'alloca' || !isNumber(item.ident)) break; item.allocatedSize = func.variables[item.assignTo].impl === VAR_EMULATED ? - calcAllocatedSize(item.allocatedType)*item.allocatedNum: 0; + calcAllocatedSize(item.allocatedType)*item.ident: 0; if (USE_TYPED_ARRAYS === 2) { // We need to keep the stack aligned item.allocatedSize = Runtime.forceAlign(item.allocatedSize, Runtime.STACK_ALIGN); @@ -1619,7 +1661,7 @@ function analyzer(data, sidePass) { var index = 0; for (var i = 0; i < lines.length; i++) { var item = lines[i]; - if (!item.assignTo || item.intertype != 'alloca' || !isNumber(item.allocatedNum)) break; + if (!item.assignTo || item.intertype != 'alloca' || !isNumber(item.ident)) break; item.allocatedIndex = index; index += item.allocatedSize; delete item.allocatedSize; @@ -1647,7 +1689,7 @@ function analyzer(data, sidePass) { for (var i = 0; i < lines.length; i++) { var item = lines[i]; - if (!finishedInitial && (!item.assignTo || item.intertype != 'alloca' || !isNumber(item.allocatedNum))) { + if (!finishedInitial && (!item.assignTo || item.intertype != 'alloca' || !isNumber(item.ident))) { finishedInitial = true; } if (item.intertype == 'alloca' && finishedInitial) { diff --git a/src/compiler.js b/src/compiler.js index 90060837..d490f454 100644 --- a/src/compiler.js +++ b/src/compiler.js @@ -185,7 +185,6 @@ if (SAFE_HEAP) USE_BSS = 0; // must initialize heap for safe heap assert(!(USE_TYPED_ARRAYS === 2 && QUANTUM_SIZE !== 4), 'For USE_TYPED_ARRAYS == 2, must have normal QUANTUM_SIZE of 4'); if (ASM_JS) { assert(!ALLOW_MEMORY_GROWTH, 'Cannot grow asm.js heap'); - assert((TOTAL_MEMORY&(TOTAL_MEMORY-1)) == 0, 'asm.js heap must be power of 2'); } assert(!(!NAMED_GLOBALS && BUILD_AS_SHARED_LIB), 'shared libraries must have named globals'); @@ -207,7 +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. -var temp = JSON.parse(read(STRUCT_INFO)); +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')); +} C_STRUCTS = temp.structs; C_DEFINES = temp.defines; @@ -312,3 +316,6 @@ if (ll_file) { } } +//var M = keys(tokenCacheMisses).map(function(m) { return [m, misses[m]] }).sort(function(a, b) { return a[1] - b[1] }); +//printErr(dump(M.slice(M.length-10))); + diff --git a/src/intertyper.js b/src/intertyper.js index 09bdaa33..781c8187 100644 --- a/src/intertyper.js +++ b/src/intertyper.js @@ -5,14 +5,16 @@ var fastPaths = 0, slowPaths = 0; +var tokenCache = {}; +['=', 'i32', 'label', ';', '4', '0', '1', '2', '255', 'align', 'i8*', 'i8', 'i16', 'getelementptr', 'inbounds', 'unnamed_addr', 'x', 'load', 'preds', 'br', 'i32*', 'i1', 'store', '<label>', 'constant', 'c', 'private', 'null', 'internal', 'to', 'bitcast', 'define', 'nounwind', 'nocapture', '%this', 'call', '...'].forEach(function(text) { tokenCache[text] = { text: text } }); + +//var tokenCacheMisses = {}; + // Line tokenizer -function tokenizer(item, inner) { - //assert(item.lineNum != 40000); - //if (item.lineNum) print(item.lineNum); +function tokenize(text, lineNum) { var tokens = []; var quotes = 0; var lastToken = null; - var CHUNKSIZE = 64; // How much forward to peek forward. Too much means too many string segments copied // Note: '{' is not an encloser, as its use in functions is split over many lines var enclosers = { '[': 0, @@ -26,22 +28,33 @@ function tokenizer(item, inner) { function makeToken(text) { if (text.length == 0) return; // merge certain tokens - if (lastToken && ( (lastToken.text == '%' && text[0] == '"') || /^\**$/.test(text) ) ) { + if (lastToken && /^\**$/.test(text)) { lastToken.text += text; return; } + var cached = tokenCache[text]; + if (cached) { + //assert(cached.text === text); + tokens.push(cached); + lastToken = cached; + return; + } + //tokenCacheMisses[text] = (misses[text] || 0) + 1; + var token = { text: text }; if (text[0] in enclosers) { - token.item = tokenizer({ - lineText: text.substr(1, text.length-2) - }, true); + token.item = tokenize(text.substr(1, text.length-2)); token.type = text[0]; } // merge certain tokens if (lastToken && isType(lastToken.text) && isFunctionDef(token)) { + if (lastToken.text in tokenCache) { + // create a copy of the cached value + lastToken = tokens[tokens.length-1] = { text: lastToken.text }; + } lastToken.text += ' ' + text; } else if (lastToken && text[0] == '}') { // }, }*, etc. var openBrace = tokens.length-1; @@ -63,7 +76,7 @@ function tokenizer(item, inner) { } } // Split using meaningful characters - var lineText = item.lineText + ' '; + var lineText = text + ' '; var re = /[\[\]\(\)<>, "]/g; var segments = lineText.split(re); segments.pop(); @@ -141,15 +154,11 @@ function tokenizer(item, inner) { var newItem = { tokens: tokens, indent: lineText.search(/[^ ]/), - lineNum: item.lineNum + lineNum: lineNum || 0 }; return newItem; } -function tokenize(text) { - return tokenizer({ lineText: text }, true); -} - // Handy sets var ENCLOSER_STARTERS = set('[', '(', '<'); @@ -251,7 +260,7 @@ function intertyper(lines, sidePass, baseLineNums) { if (mainPass && /^}.*/.test(line)) { inFunction = false; if (mainPass) { - var func = funcHeaderHandler(tokenizer({ lineText: currFunctionLines[0], lineNum: currFunctionLineNum }, true)); + var func = funcHeaderHandler(tokenize(currFunctionLines[0], currFunctionLineNum)); if (SKIP_STACK_IN_SMALL && /emscripten_autodebug/.exec(func.ident)) { warnOnce('Disabling SKIP_STACK_IN_SMALL because we are apparently processing autodebugger data'); @@ -766,15 +775,13 @@ function intertyper(lines, sidePass, baseLineNums) { return item; } // 'alloca' - var allocaPossibleVars = ['allocatedNum']; function allocaHandler(item) { item.intertype = 'alloca'; item.allocatedType = item.tokens[1].text; if (item.tokens.length > 3 && Runtime.isNumberType(item.tokens[3].text)) { - item.allocatedNum = toNiceIdent(item.tokens[4].text); - item.possibleVars = allocaPossibleVars; + item.ident = toNiceIdent(item.tokens[4].text); } else { - item.allocatedNum = 1; + item.ident = 1; } item.type = addPointing(item.tokens[1].text); // type of pointer we will get Types.needAnalysis[item.type] = 0; @@ -1099,7 +1106,7 @@ function intertyper(lines, sidePass, baseLineNums) { if (ret) { if (COMPILER_ASSERTIONS) { //printErr(['\n', dump(ret), '\n', dump(triager(tokenizer(line)))]); - var normal = triager(tokenizer(line)); + var normal = triager(tokenize(line)); delete normal.tokens; delete normal.indent; assert(sortedJsonCompare(normal, ret), 'fast path: ' + dump(normal) + '\n vs \n' + dump(ret)); @@ -1124,7 +1131,7 @@ function intertyper(lines, sidePass, baseLineNums) { //var time = Date.now(); - var t = tokenizer(line); + var t = tokenize(line.lineText, line.lineNum); item = triager(t); /* diff --git a/src/jsifier.js b/src/jsifier.js index 0e5f8ef3..f638ea08 100644 --- a/src/jsifier.js +++ b/src/jsifier.js @@ -234,8 +234,8 @@ function JSify(data, functionsOnly, givenFunctions) { function globalVariableHandler(item) { function needsPostSet(value) { if (typeof value !== 'string') return false; - return value[0] in UNDERSCORE_OPENPARENS || value.substr(0, 14) === 'CHECK_OVERFLOW' - || value.substr(0, 6) === 'GLOBAL'; + // (' is ok, as it is something we can indexize later into a concrete int: ('{{ FI_ ... + return /^([(_][^']|CHECK_OVERFLOW|GLOBAL).*/.test(value); } item.intertype = 'GlobalVariableStub'; @@ -308,6 +308,8 @@ function JSify(data, functionsOnly, givenFunctions) { JS: makeSetValue(makeGlobalUse(item.ident), i, value, structTypes[i], false, true) + ';' // ignore=true, since e.g. rtti and statics cause lots of safe_heap errors }); constant[i] = '0'; + } else { + if (typeof value === 'string') constant[i] = deParenCarefully(value); } }); @@ -405,6 +407,9 @@ function JSify(data, functionsOnly, givenFunctions) { var snippet = LibraryManager.library[ident]; var redirectedIdent = null; var deps = LibraryManager.library[ident + '__deps'] || []; + deps.forEach(function(dep) { + if (typeof snippet === 'string' && !(dep in LibraryManager.library)) warn('missing library dependency ' + dep + ', make sure you are compiling with the right options (see #ifdefs in src/library*.js)'); + }); var isFunction = false; if (typeof snippet === 'string') { @@ -536,7 +541,6 @@ function JSify(data, functionsOnly, givenFunctions) { case 'unreachable': line.JS = unreachableHandler(line); break; default: throw 'what is this line? ' + dump(line); } - assert(line.JS); //if (ASM_JS) assert(line.JS.indexOf('var ') < 0, dump(line)); if (line.assignTo) makeAssign(line); Framework.currItem = null; @@ -1361,7 +1365,7 @@ function JSify(data, functionsOnly, givenFunctions) { if (item.allocatedSize === 0) return ''; // This will not actually be shown - it's nativized return asmCoercion(getFastValue('sp', '+', item.allocatedIndex.toString()), 'i32'); } else { - return RuntimeGenerator.stackAlloc(getFastValue(calcAllocatedSize(item.allocatedType), '*', item.allocatedNum)); + return RuntimeGenerator.stackAlloc(getFastValue(calcAllocatedSize(item.allocatedType), '*', item.ident)); } } function va_argHandler(item) { diff --git a/src/library.js b/src/library.js index 5e71b087..875d8bab 100644 --- a/src/library.js +++ b/src/library.js @@ -4296,16 +4296,16 @@ LibraryManager.library = { }, llvm_trap: function() { - throw 'trap! ' + new Error().stack; + abort('trap!'); }, __assert_fail: function(condition, filename, line, func) { ABORT = true; - throw 'Assertion failed: ' + Pointer_stringify(condition) + ', at: ' + [filename ? Pointer_stringify(filename) : 'unknown filename', line, func ? Pointer_stringify(func) : 'unknown function'] + ' at ' + new Error().stack; + throw 'Assertion failed: ' + Pointer_stringify(condition) + ', at: ' + [filename ? Pointer_stringify(filename) : 'unknown filename', line, func ? Pointer_stringify(func) : 'unknown function'] + ' at ' + stackTrace(); }, __assert_func: function(filename, line, func, condition) { - throw 'Assertion failed: ' + (condition ? Pointer_stringify(condition) : 'unknown condition') + ', at: ' + [filename ? Pointer_stringify(filename) : 'unknown filename', line, func ? Pointer_stringify(func) : 'unknown function'] + ' at ' + new Error().stack; + throw 'Assertion failed: ' + (condition ? Pointer_stringify(condition) : 'unknown condition') + ', at: ' + [filename ? Pointer_stringify(filename) : 'unknown filename', line, func ? Pointer_stringify(func) : 'unknown function'] + ' at ' + stackTrace(); }, __cxa_guard_acquire: function(variable) { @@ -4353,7 +4353,7 @@ LibraryManager.library = { ___cxa_throw.initialized = true; } #if EXCEPTION_DEBUG - Module.printErr('Compiled code throwing an exception, ' + [ptr,type,destructor] + ', at ' + new Error().stack); + Module.printErr('Compiled code throwing an exception, ' + [ptr,type,destructor] + ', at ' + stackTrace()); #endif {{{ makeSetValue('_llvm_eh_exception.buf', '0', 'ptr', 'void*') }}} {{{ makeSetValue('_llvm_eh_exception.buf', QUANTUM_SIZE, 'type', 'void*') }}} diff --git a/src/library_browser.js b/src/library_browser.js index cba8ecdf..dd60a581 100644 --- a/src/library_browser.js +++ b/src/library_browser.js @@ -749,6 +749,7 @@ mergeInto(LibraryManager.library, { if (e instanceof ExitStatus) { return; } else { + if (e && typeof e === 'object' && e.stack) Module.printErr('exception thrown: ' + [e, e.stack]); throw e; } } diff --git a/src/library_fs.js b/src/library_fs.js index dc5c20f8..da1a4e7f 100644 --- a/src/library_fs.js +++ b/src/library_fs.js @@ -1,5 +1,5 @@ mergeInto(LibraryManager.library, { - $FS__deps: ['$ERRNO_CODES', '$ERRNO_MESSAGES', '__setErrNo', '$VFS', '$PATH', '$TTY', '$MEMFS', '$IDBFS', '$NODEFS', 'stdin', 'stdout', 'stderr', 'fflush'], + $FS__deps: ['$ERRNO_CODES', '$ERRNO_MESSAGES', '__setErrNo', '$PATH', '$TTY', '$MEMFS', '$IDBFS', '$NODEFS', 'stdin', 'stdout', 'stderr', 'fflush'], $FS__postset: 'FS.staticInit();' + '__ATINIT__.unshift({ func: function() { if (!Module["noFSInit"] && !FS.init.initialized) FS.init() } });' + '__ATMAIN__.push({ func: function() { FS.ignorePermissions = false } });' + @@ -27,24 +27,10 @@ mergeInto(LibraryManager.library, { // to modify the filesystem freely before run() is called. ignorePermissions: true, - ErrnoError: (function() { - function ErrnoError(errno) { - this.errno = errno; - for (var key in ERRNO_CODES) { - if (ERRNO_CODES[key] === errno) { - this.code = key; - break; - } - } - this.message = ERRNO_MESSAGES[errno]; - }; - ErrnoError.prototype = new Error(); - ErrnoError.prototype.constructor = ErrnoError; - return ErrnoError; - }()), + ErrnoError: null, // set during init handleFSError: function(e) { - if (!(e instanceof FS.ErrnoError)) throw e + ' : ' + new Error().stack; + if (!(e instanceof FS.ErrnoError)) throw e + ' : ' + stackTrace(); return ___setErrNo(e.errno); }, @@ -1091,6 +1077,20 @@ mergeInto(LibraryManager.library, { assert(!FS.init.initialized, 'FS.init was previously called. If you want to initialize later with custom parameters, remove any earlier calls (note that one is automatically added to the generated code)'); FS.init.initialized = true; + FS.ErrnoError = function ErrnoError(errno) { + this.errno = errno; + for (var key in ERRNO_CODES) { + if (ERRNO_CODES[key] === errno) { + this.code = key; + break; + } + } + this.message = ERRNO_MESSAGES[errno]; + this.stack = stackTrace(); + }; + FS.ErrnoError.prototype = new Error(); + FS.ErrnoError.prototype.constructor = FS.ErrnoError; + // Allow Module.stdin etc. to provide defaults, if none explicitly passed to us here Module['stdin'] = input || Module['stdin']; Module['stdout'] = output || Module['stdout']; diff --git a/src/library_gl.js b/src/library_gl.js index 511709ae..1ea8efc2 100644 --- a/src/library_gl.js +++ b/src/library_gl.js @@ -374,6 +374,9 @@ var LibraryGL = { }, #endif + // In WebGL, extensions must be explicitly enabled to be active, see http://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.14 + // In GLES2, all extensions are enabled by default without additional operations. Init all extensions we need to give to GLES2 user + // code here, so that GLES2 code can operate without changing behavior. initExtensions: function() { if (GL.initExtensions.done) return; GL.initExtensions.done = true; @@ -394,6 +397,7 @@ var LibraryGL = { GL.generateTempBuffers(); #endif + // Detect the presence of a few extensions manually, this GL interop layer itself will need to know if they exist. GL.compressionExt = Module.ctx.getExtension('WEBGL_compressed_texture_s3tc') || Module.ctx.getExtension('MOZ_WEBGL_compressed_texture_s3tc') || Module.ctx.getExtension('WEBKIT_WEBGL_compressed_texture_s3tc'); @@ -404,12 +408,80 @@ var LibraryGL = { GL.floatExt = Module.ctx.getExtension('OES_texture_float'); - GL.elementIndexUintExt = Module.ctx.getExtension('OES_element_index_uint'); - GL.standardDerivativesExt = Module.ctx.getExtension('OES_standard_derivatives'); + // These are the 'safe' feature-enabling extensions that don't add any performance impact related to e.g. debugging, and + // should be enabled by default so that client GLES2/GL code will not need to go through extra hoops to get its stuff working. + // As new extensions are ratified at http://www.khronos.org/registry/webgl/extensions/ , feel free to add your new extensions + // here, as long as they don't produce a performance impact for users that might not be using those extensions. + // E.g. debugging-related extensions should probably be off by default. + var automaticallyEnabledExtensions = [ "OES_texture_float", "OES_texture_half_float", "OES_standard_derivatives", + "OES_vertex_array_object", "WEBGL_compressed_texture_s3tc", "WEBGL_depth_texture", + "OES_element_index_uint", "EXT_texture_filter_anisotropic", "ANGLE_instanced_arrays", + "OES_texture_float_linear", "OES_texture_half_float_linear", "WEBGL_compressed_texture_atc", + "WEBGL_compressed_texture_pvrtc", "EXT_color_buffer_half_float", "WEBGL_color_buffer_float", + "EXT_frag_depth", "EXT_sRGB", "WEBGL_draw_buffers", "WEBGL_shared_resources" ]; + + function shouldEnableAutomatically(extension) { + for(var i in automaticallyEnabledExtensions) { + var include = automaticallyEnabledExtensions[i]; + if (ext.indexOf(include) != -1) { + return true; + } + } + return false; + } + + var extensions = Module.ctx.getSupportedExtensions(); + for(var e in extensions) { + var ext = extensions[e].replace('MOZ_', '').replace('WEBKIT_', ''); + if (automaticallyEnabledExtensions.indexOf(ext) != -1) { + Module.ctx.getExtension(ext); // Calling .getExtension enables that extension permanently, no need to store the return value to be enabled. + } + } + }, - GL.depthTextureExt = Module.ctx.getExtension("WEBGL_depth_texture") || - Module.ctx.getExtension("MOZ_WEBGL_depth_texture") || - Module.ctx.getExtension("WEBKIT_WEBGL_depth_texture"); + // In WebGL, uniforms in a shader program are accessed through an opaque object type 'WebGLUniformLocation'. + // In GLES2, uniforms are accessed via indices. Therefore we must generate a mapping of indices -> WebGLUniformLocations + // to provide the client code the API that uses indices. + // This function takes a linked GL program and generates a mapping table for the program. + // NOTE: Populating the uniform table is performed eagerly at glLinkProgram time, so glLinkProgram should be considered + // to be a slow/costly function call. Calling glGetUniformLocation is relatively fast, since it is always a read-only + // lookup to the table populated in this function call. + populateUniformTable: function(program) { +#if GL_ASSERTIONS + 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. + // 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; + // Strip off any trailing array specifier we might have got, e.g. "[0]". + if (name.indexOf(']', name.length-1) !== -1) { + var ls = name.lastIndexOf('['); + name = name.slice(0, ls); + } + + // 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. + // 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]; + GL.uniforms[id] = loc; + + for (var j = 1; j < u.size; ++j) { + var n = name + '['+j+']'; + loc = Module.ctx.getUniformLocation(p, n); + id = GL.getNewId(GL.uniforms); + + GL.uniforms[id] = loc; + } + } } }, @@ -431,7 +503,13 @@ var LibraryGL = { case 0x1F02 /* GL_VERSION */: return allocate(intArrayFromString(Module.ctx.getParameter(name_)), 'i8', ALLOC_NORMAL); case 0x1F03 /* GL_EXTENSIONS */: - return allocate(intArrayFromString(Module.ctx.getSupportedExtensions().join(' ')), 'i8', ALLOC_NORMAL); + var exts = Module.ctx.getSupportedExtensions(); + var gl_exts = []; + for (i in exts) { + 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. case 0x8B8C /* GL_SHADING_LANGUAGE_VERSION */: return allocate(intArrayFromString('OpenGL ES GLSL 1.00 (WebGL)'), 'i8', ALLOC_NORMAL); default: @@ -811,6 +889,7 @@ var LibraryGL = { glGetUniformfv: function(program, location, params) { #if GL_ASSERTIONS GL.validateGLObjectID(GL.programs, program, 'glGetUniformfv', 'program'); + GL.validateGLObjectID(GL.uniforms, location, 'glGetUniformfv', 'location'); #endif var data = Module.ctx.getUniform(GL.programs[program], GL.uniforms[location]); if (typeof data == 'number') { @@ -826,6 +905,7 @@ var LibraryGL = { glGetUniformiv: function(program, location, params) { #if GL_ASSERTIONS GL.validateGLObjectID(GL.programs, program, 'glGetUniformiv', 'program'); + GL.validateGLObjectID(GL.uniforms, location, 'glGetUniformiv', 'location'); #endif var data = Module.ctx.getUniform(GL.programs[program], GL.uniforms[location]); if (typeof data == 'number' || typeof data == 'boolean') { @@ -843,16 +923,31 @@ var LibraryGL = { GL.validateGLObjectID(GL.programs, program, 'glGetUniformLocation', 'program'); #endif name = Pointer_stringify(name); + + var arrayOffset = 0; + // If user passed an array accessor "[index]", parse the array index off the accessor. + if (name.indexOf(']', name.length-1) !== -1) { + var ls = name.lastIndexOf('['); + var arrayIndex = name.slice(ls+1, -1); + if (arrayIndex.length > 0) { + arrayOffset = parseInt(arrayIndex); + if (arrayOffset < 0) { + return -1; + } + } + name = name.slice(0, ls); + } + var ptable = GL.uniformTable[program]; - if (!ptable) ptable = GL.uniformTable[program] = {}; - var id = ptable[name]; - if (id) return id; - var loc = Module.ctx.getUniformLocation(GL.programs[program], name); - if (!loc) return -1; - id = GL.getNewId(GL.uniforms); - GL.uniforms[id] = loc; - ptable[name] = id; - return id; + if (!ptable) { + return -1; + } + var uniformInfo = ptable[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 { + return -1; + } }, glGetVertexAttribfv__sig: 'viii', @@ -923,54 +1018,81 @@ var LibraryGL = { glUniform1f__sig: 'vif', glUniform1f: function(location, v0) { +#if GL_ASSERTIONS + GL.validateGLObjectID(GL.uniforms, location, 'glUniform1f', 'location'); +#endif location = GL.uniforms[location]; Module.ctx.uniform1f(location, v0); }, glUniform2f__sig: 'viff', glUniform2f: function(location, v0, v1) { +#if GL_ASSERTIONS + GL.validateGLObjectID(GL.uniforms, location, 'glUniform2f', 'location'); +#endif location = GL.uniforms[location]; Module.ctx.uniform2f(location, v0, v1); }, glUniform3f__sig: 'vifff', glUniform3f: function(location, v0, v1, v2) { +#if GL_ASSERTIONS + GL.validateGLObjectID(GL.uniforms, location, 'glUniform3f', 'location'); +#endif location = GL.uniforms[location]; Module.ctx.uniform3f(location, v0, v1, v2); }, glUniform4f__sig: 'viffff', glUniform4f: function(location, v0, v1, v2, v3) { +#if GL_ASSERTIONS + GL.validateGLObjectID(GL.uniforms, location, 'glUniform4f', 'location'); +#endif location = GL.uniforms[location]; Module.ctx.uniform4f(location, v0, v1, v2, v3); }, glUniform1i__sig: 'vii', glUniform1i: function(location, v0) { +#if GL_ASSERTIONS + GL.validateGLObjectID(GL.uniforms, location, 'glUniform1i', 'location'); +#endif location = GL.uniforms[location]; Module.ctx.uniform1i(location, v0); }, glUniform2i__sig: 'viii', glUniform2i: function(location, v0, v1) { +#if GL_ASSERTIONS + GL.validateGLObjectID(GL.uniforms, location, 'glUniform2i', 'location'); +#endif location = GL.uniforms[location]; Module.ctx.uniform2i(location, v0, v1); }, glUniform3i__sig: 'viiii', glUniform3i: function(location, v0, v1, v2) { +#if GL_ASSERTIONS + GL.validateGLObjectID(GL.uniforms, location, 'glUniform3i', 'location'); +#endif location = GL.uniforms[location]; Module.ctx.uniform3i(location, v0, v1, v2); }, glUniform4i__sig: 'viiiii', glUniform4i: function(location, v0, v1, v2, v3) { +#if GL_ASSERTIONS + GL.validateGLObjectID(GL.uniforms, location, 'glUniform4i', 'location'); +#endif location = GL.uniforms[location]; Module.ctx.uniform4i(location, v0, v1, v2, v3); }, glUniform1iv__sig: 'viii', glUniform1iv: function(location, count, value) { +#if GL_ASSERTIONS + GL.validateGLObjectID(GL.uniforms, location, 'glUniform1iv', 'location'); +#endif location = GL.uniforms[location]; value = {{{ makeHEAPView('32', 'value', 'value+count*4') }}}; Module.ctx.uniform1iv(location, value); @@ -978,6 +1100,9 @@ var LibraryGL = { glUniform2iv__sig: 'viii', glUniform2iv: function(location, count, value) { +#if GL_ASSERTIONS + GL.validateGLObjectID(GL.uniforms, location, 'glUniform2iv', 'location'); +#endif location = GL.uniforms[location]; count *= 2; value = {{{ makeHEAPView('32', 'value', 'value+count*4') }}}; @@ -986,6 +1111,9 @@ var LibraryGL = { glUniform3iv__sig: 'viii', glUniform3iv: function(location, count, value) { +#if GL_ASSERTIONS + GL.validateGLObjectID(GL.uniforms, location, 'glUniform3iv', 'location'); +#endif location = GL.uniforms[location]; count *= 3; value = {{{ makeHEAPView('32', 'value', 'value+count*4') }}}; @@ -994,6 +1122,9 @@ var LibraryGL = { glUniform4iv__sig: 'viii', glUniform4iv: function(location, count, value) { +#if GL_ASSERTIONS + GL.validateGLObjectID(GL.uniforms, location, 'glUniform4iv', 'location'); +#endif location = GL.uniforms[location]; count *= 4; value = {{{ makeHEAPView('32', 'value', 'value+count*4') }}}; @@ -1002,6 +1133,9 @@ var LibraryGL = { glUniform1fv__sig: 'viii', glUniform1fv: function(location, count, value) { +#if GL_ASSERTIONS + GL.validateGLObjectID(GL.uniforms, location, 'glUniform1fv', 'location'); +#endif location = GL.uniforms[location]; var view; if (count == 1) { @@ -1016,6 +1150,9 @@ var LibraryGL = { glUniform2fv__sig: 'viii', glUniform2fv: function(location, count, value) { +#if GL_ASSERTIONS + GL.validateGLObjectID(GL.uniforms, location, 'glUniform2fv', 'location'); +#endif location = GL.uniforms[location]; var view; if (count == 1) { @@ -1031,6 +1168,9 @@ var LibraryGL = { glUniform3fv__sig: 'viii', glUniform3fv: function(location, count, value) { +#if GL_ASSERTIONS + GL.validateGLObjectID(GL.uniforms, location, 'glUniform3fv', 'location'); +#endif location = GL.uniforms[location]; var view; if (count == 1) { @@ -1047,6 +1187,9 @@ var LibraryGL = { glUniform4fv__sig: 'viii', glUniform4fv: function(location, count, value) { +#if GL_ASSERTIONS + GL.validateGLObjectID(GL.uniforms, location, 'glUniform4fv', 'location'); +#endif location = GL.uniforms[location]; var view; if (count == 1) { @@ -1064,6 +1207,9 @@ var LibraryGL = { glUniformMatrix2fv__sig: 'viiii', glUniformMatrix2fv: function(location, count, transpose, value) { +#if GL_ASSERTIONS + GL.validateGLObjectID(GL.uniforms, location, 'glUniformMatrix2fv', 'location'); +#endif location = GL.uniforms[location]; var view; if (count == 1) { @@ -1080,6 +1226,9 @@ var LibraryGL = { glUniformMatrix3fv__sig: 'viiii', glUniformMatrix3fv: function(location, count, transpose, value) { +#if GL_ASSERTIONS + GL.validateGLObjectID(GL.uniforms, location, 'glUniformMatrix3fv', 'location'); +#endif location = GL.uniforms[location]; var view; if (count == 1) { @@ -1096,6 +1245,9 @@ var LibraryGL = { glUniformMatrix4fv__sig: 'viiii', glUniformMatrix4fv: function(location, count, transpose, value) { +#if GL_ASSERTIONS + GL.validateGLObjectID(GL.uniforms, location, 'glUniformMatrix4fv', 'location'); +#endif location = GL.uniforms[location]; var view; if (count == 1) { @@ -1340,6 +1492,7 @@ var LibraryGL = { #endif Module.ctx.linkProgram(GL.programs[program]); GL.uniformTable[program] = {}; // uniforms no longer keep the same names after linking + GL.populateUniformTable(program); }, glGetProgramInfoLog__sig: 'viiii', @@ -4320,6 +4473,7 @@ var LibraryGL = { return 1 /* GL_TRUE */; }, + gluOrtho2D__deps: ['glOrtho'], gluOrtho2D: function(left, right, bottom, top) { _glOrtho(left, right, bottom, top, -1, 1); }, diff --git a/src/library_glut.js b/src/library_glut.js index 60dc6540..2321486a 100644 --- a/src/library_glut.js +++ b/src/library_glut.js @@ -189,12 +189,12 @@ var LibraryGLUT = { } }, - onMouseButtonDown: function(event){ + onMouseButtonDown: function(event) { Browser.calculateMouseEvent(event); GLUT.buttons |= (1 << event['button']); - if(event.target == Module["canvas"] && GLUT.mouseFunc){ + if (event.target == Module["canvas"] && GLUT.mouseFunc) { try { event.target.setCapture(); } catch (e) {} @@ -204,21 +204,40 @@ var LibraryGLUT = { } }, - onMouseButtonUp: function(event){ + onMouseButtonUp: function(event) { Browser.calculateMouseEvent(event); GLUT.buttons &= ~(1 << event['button']); - if(GLUT.mouseFunc) { + if (GLUT.mouseFunc) { event.preventDefault(); GLUT.saveModifiers(event); Runtime.dynCall('viiii', GLUT.mouseFunc, [event['button'], 1/*GLUT_UP*/, Browser.mouseX, Browser.mouseY]); } }, + onMouseWheel: function(event) { + Browser.calculateMouseEvent(event); + + // cross-browser wheel delta + var e = window.event || event; // old IE support + var delta = Math.max(-1, Math.min(1, (e.wheelDelta || -e.detail))); + + var button = 3; // wheel up + 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){ + onFullScreenEventChange: function(event) { var width; var height; if (document["fullScreen"] || document["mozFullScreen"] || document["webkitIsFullScreen"]) { @@ -279,6 +298,10 @@ var LibraryGLUT = { window.addEventListener("mousemove", GLUT.onMousemove, true); window.addEventListener("mousedown", GLUT.onMouseButtonDown, true); window.addEventListener("mouseup", GLUT.onMouseButtonUp, true); + // IE9, Chrome, Safari, Opera + window.addEventListener("mousewheel", GLUT.onMouseWheel, true); + // Firefox + window.addEventListener("DOMMouseScroll", GLUT.onMouseWheel, true); } Browser.resizeListeners.push(function(width, height) { @@ -298,6 +321,10 @@ var LibraryGLUT = { window.removeEventListener("mousemove", GLUT.onMousemove, true); window.removeEventListener("mousedown", GLUT.onMouseButtonDown, true); window.removeEventListener("mouseup", GLUT.onMouseButtonUp, true); + // IE9, Chrome, Safari, Opera + window.removeEventListener("mousewheel", GLUT.onMouseWheel, true); + // Firefox + window.removeEventListener("DOMMouseScroll", GLUT.onMouseWheel, true); } Module["canvas"].width = Module["canvas"].height = 1; } }); diff --git a/src/library_memfs.js b/src/library_memfs.js index 4e56d996..94fd767e 100644 --- a/src/library_memfs.js +++ b/src/library_memfs.js @@ -140,6 +140,7 @@ mergeInto(LibraryManager.library, { delete old_node.parent.contents[old_node.name]; old_node.name = new_name; new_dir.contents[new_name] = old_node; + old_node.parent = new_dir; }, unlink: function(parent, name) { delete parent.contents[name]; diff --git a/src/library_sdl.js b/src/library_sdl.js index 27f2c0da..e64f117f 100644 --- a/src/library_sdl.js +++ b/src/library_sdl.js @@ -1122,7 +1122,7 @@ var LibrarySDL = { var ret = SDL.makeSurface(diagonal, diagonal, srcData.flags, false, 'rotozoomSurface'); var dstData = SDL.surfaces[ret]; dstData.ctx.translate(diagonal / 2, diagonal / 2); - dstData.ctx.rotate(angle * Math.PI / 180); + dstData.ctx.rotate(-angle * Math.PI / 180); dstData.ctx.drawImage(srcData.canvas, -w / 2, -h / 2, w, h); return ret; }, diff --git a/src/modules.js b/src/modules.js index cc9ca549..76e5db11 100644 --- a/src/modules.js +++ b/src/modules.js @@ -285,11 +285,7 @@ var Functions = { } else { if (!singlePhase) return 'NO_INDEX'; // Should not index functions in post ret = this.indexedFunctions[ident]; - if (!ret) { - ret = this.nextIndex; - this.nextIndex += FUNCTION_POINTER_ALIGNMENT; - this.indexedFunctions[ident] = ret; - } + assert(ret); ret = ret.toString(); } if (SIDE_MODULE && sig) { // sig can be undefined for the GL library functions diff --git a/src/parseTools.js b/src/parseTools.js index addf0f21..e3b1df6d 100644 --- a/src/parseTools.js +++ b/src/parseTools.js @@ -280,7 +280,7 @@ function isFunctionType(type, out) { i--; } assert(argText); - return isFunctionDef({ text: argText, item: tokenize(argText.substr(1, argText.length-2), true) }, out); + return isFunctionDef({ text: argText, item: tokenize(argText.substr(1, argText.length-2)) }, out); } function getReturnType(type) { @@ -914,13 +914,13 @@ function parseI64Constant(str, legalized) { } function parseNumerical(value, type) { - if ((!type || type == 'double' || type == 'float') && (value.substr && value.substr(0,2) == '0x')) { + if ((!type || type === 'double' || type === 'float') && /^0x/.test(value)) { // Hexadecimal double value, as the llvm docs say, // "The one non-intuitive notation for constants is the hexadecimal form of floating point constants." value = IEEEUnHex(value); } else if (USE_TYPED_ARRAYS == 2 && isIllegalType(type)) { return value; // do not parseFloat etc., that can lead to loss of precision - } else if (value == 'null') { + } else if (value === 'null') { // NULL *is* 0, in C/C++. No JS null! (null == 0 is false, etc.) value = '0'; } else if (value === 'true') { @@ -930,7 +930,7 @@ function parseNumerical(value, type) { } if (isNumber(value)) { var ret = parseFloat(value); // will change e.g. 5.000000e+01 to 50 - if (type in Runtime.FLOAT_TYPES) { + if (type === 'double' || type === 'float') { if (value[0] === '-' && ret === 0) return '-.0'; // fix negative 0, toString makes it 0 if (!RUNNING_JS_OPTS) ret = asmEnsureFloat(ret, type); } @@ -2474,13 +2474,6 @@ function walkInterdata(item, pre, post, obj) { if (walkInterdata(item.params[i], pre, post, obj)) return true; } } - if (item.possibleVars) { // other attributes that might contain interesting data; here, variables - var box = { intertype: 'value', ident: '' }; - for (i = 0; i <= item.possibleVars.length; i++) { - box.ident = item[item.possibleVars[i]]; - if (walkInterdata(box, pre, post, obj)) return true; - } - } return post && post(item, originalObj, obj); } @@ -2500,7 +2493,6 @@ function walkAndModifyInterdata(item, pre) { if (repl = walkAndModifyInterdata(item.params[i], pre)) item.params[i] = repl; } } - // Ignore possibleVars because we can't replace them anyhow } function parseBlockAddress(segment) { @@ -2582,6 +2574,11 @@ function deParen(text) { return text; } +function deParenCarefully(text) { + if (text[0] === '(' && text.indexOf('(', 1) < 0 && text[text.length-1] === ')') return text.substr(1, text.length-2); + return text; +} + function addVariable(ident, type, funcData) { funcData = funcData || Framework.currItem.funcData; assert(type); diff --git a/src/postamble.js b/src/postamble.js index cd892733..d64fb220 100644 --- a/src/postamble.js +++ b/src/postamble.js @@ -96,6 +96,7 @@ Module['callMain'] = Module.callMain = function callMain(args) { Module['noExitRuntime'] = true; return; } else { + if (e && typeof e === 'object' && e.stack) Module.printErr('exception thrown: ' + [e, e.stack]); throw e; } } finally { @@ -179,7 +180,7 @@ function abort(text) { ABORT = true; EXITSTATUS = 1; - throw 'abort() at ' + (new Error().stack); + throw 'abort() at ' + stackTrace(); } Module['abort'] = Module.abort = abort; diff --git a/src/preamble.js b/src/preamble.js index 88aaff77..aedb0e7c 100644 --- a/src/preamble.js +++ b/src/preamble.js @@ -36,7 +36,7 @@ var SAFE_HEAP_ERRORS = 0; var ACCEPTABLE_SAFE_HEAP_ERRORS = 0; function SAFE_HEAP_ACCESS(dest, type, store, ignore, storeValue) { - //if (dest === A_NUMBER) Module.print ([dest, type, store, ignore, storeValue] + ' ' + new Error().stack); // Something like this may be useful, in debugging + //if (dest === A_NUMBER) Module.print ([dest, type, store, ignore, storeValue] + ' ' + stackTrace()); // Something like this may be useful, in debugging assert(dest > 0, 'segmentation fault'); @@ -63,7 +63,7 @@ function SAFE_HEAP_ACCESS(dest, type, store, ignore, storeValue) { try { if (HEAP[dest].toString() === 'NaN') error = false; // NaN is acceptable, as a double value } catch(e){} - if (error) throw('Warning: Reading an invalid value at ' + dest + ' :: ' + new Error().stack + '\n'); + if (error) throw('Warning: Reading an invalid value at ' + dest + ' :: ' + stackTrace() + '\n'); } #endif if (type === null) return; @@ -72,14 +72,14 @@ function SAFE_HEAP_ACCESS(dest, type, store, ignore, storeValue) { if (!ignore) assert(history, 'Must have a history for a safe heap load! ' + dest + ':' + type); // Warning - bit fields in C structs cause loads+stores for each store, so // they will show up here... -// assert((history && history[0]) /* || HEAP[dest] === 0 */, "Loading from where there was no store! " + dest + ',' + HEAP[dest] + ',' + type + ', \n\n' + new Error().stack + '\n'); +// assert((history && history[0]) /* || HEAP[dest] === 0 */, "Loading from where there was no store! " + dest + ',' + HEAP[dest] + ',' + type + ', \n\n' + stackTrace() + '\n'); // if (history[0].type !== type) { if (history !== type && !ignore) { Module.print('Load-store consistency assumption failure! ' + dest); Module.print('\n'); Module.print(JSON.stringify(history)); Module.print('\n'); - Module.print('LOAD: ' + type + ', ' + new Error().stack); + Module.print('LOAD: ' + type + ', ' + stackTrace()); Module.print('\n'); SAFE_HEAP_ERRORS++; assert(SAFE_HEAP_ERRORS <= ACCEPTABLE_SAFE_HEAP_ERRORS, 'Load-store consistency assumption failure!'); @@ -93,9 +93,9 @@ function SAFE_HEAP_STORE(dest, value, type, ignore) { #endif if (!ignore && !value && (value === null || value === undefined)) { - throw('Warning: Writing an invalid value of ' + JSON.stringify(value) + ' at ' + dest + ' :: ' + new Error().stack + '\n'); + throw('Warning: Writing an invalid value of ' + JSON.stringify(value) + ' at ' + dest + ' :: ' + stackTrace() + '\n'); } - //if (!ignore && (value === Infinity || value === -Infinity || isNaN(value))) throw [value, typeof value, new Error().stack]; + //if (!ignore && (value === Infinity || value === -Infinity || isNaN(value))) throw [value, typeof value, stackTrace()]; SAFE_HEAP_ACCESS(dest, type, true, ignore, value); if (dest in HEAP_WATCHED) { @@ -640,6 +640,143 @@ function stringToUTF32(str, outPtr) { } Module['stringToUTF32'] = stringToUTF32; +function demangle(func) { + try { + if (typeof func === 'number') func = Pointer_stringify(func); + if (func[0] !== '_') return func; + if (func[1] !== '_') return func; // C function + if (func[2] !== 'Z') return func; + var i = 3; + // params, etc. + var basicTypes = { + 'v': 'void', + 'b': 'bool', + 'c': 'char', + 's': 'short', + 'i': 'int', + 'l': 'long', + 'f': 'float', + 'd': 'double', + 'w': 'wchar_t', + 'a': 'signed char', + 'h': 'unsigned char', + 't': 'unsigned short', + 'j': 'unsigned int', + 'm': 'unsigned long', + 'x': 'long long', + 'y': 'unsigned long long', + 'z': '...' + }; + function dump(x) { + //return; + if (x) Module.print(x); + Module.print(func); + var pre = ''; + for (var a = 0; a < i; a++) pre += ' '; + Module.print (pre + '^'); + } + var subs = []; + function parseNested() { + i++; + if (func[i] === 'K') i++; + var parts = []; + while (func[i] !== 'E') { + if (func[i] === 'S') { // substitution + i++; + var next = func.indexOf('_', i); + var num = func.substring(i, next) || 0; + parts.push(subs[num] || '?'); + i = next+1; + continue; + } + var size = parseInt(func.substr(i)); + var pre = size.toString().length; + if (!size || !pre) { i--; break; } // counter i++ below us + var curr = func.substr(i + pre, size); + parts.push(curr); + subs.push(curr); + i += pre + size; + } + i++; // skip E + return parts; + } + function parse(rawList, limit, allowVoid) { // main parser + limit = limit || Infinity; + var ret = '', list = []; + function flushList() { + return '(' + list.join(', ') + ')'; + } + var name; + if (func[i] !== 'N') { + // not namespaced + if (func[i] === 'K') i++; + 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; + } + if (func[i] === 'I') { + i++; + var iList = parse(true); + var iRet = parse(true, 1, true); + ret += iRet[0] + ' ' + name + '<' + iList.join(', ') + '>'; + } else { + ret = name; + } + paramLoop: while (i < func.length && limit-- > 0) { + //dump('paramLoop'); + var c = func[i++]; + if (c in basicTypes) { + list.push(basicTypes[c]); + } else { + switch (c) { + case 'P': list.push(parse(true, 1, true)[0] + '*'); break; // pointer + case 'R': list.push(parse(true, 1, true)[0] + '&'); break; // reference + case 'L': { // literal + i++; // skip basic type + var end = func.indexOf('E', i); + var size = end - i; + list.push(func.substr(i, size)); + i += size + 2; // size + 'EE' + break; + } + case 'A': { // array + var size = parseInt(func.substr(i)); + i += size.toString().length; + if (func[i] !== '_') throw '?'; + i++; // skip _ + list.push(parse(true, 1, true)[0] + ' [' + size + ']'); + break; + } + case 'E': break paramLoop; + default: ret += '?' + c; break paramLoop; + } + } + } + if (!allowVoid && list.length === 1 && list[0] === 'void') list = []; // avoid (void) + return rawList ? list : ret + flushList(); + } + return parse(); + } catch(e) { + return func; + } +} + +function demangleAll(text) { + return text.replace(/__Z[\w\d_]+/g, function(x) { var y = demangle(x); return x === y ? x : (x + ' [' + y + ']') }); +} + +function stackTrace() { + return demangleAll(new Error().stack); +} + // Memory management var PAGE_SIZE = 4096; |