diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/analyzer.js | 7 | ||||
-rw-r--r-- | src/compiler.js | 10 | ||||
-rw-r--r-- | src/corruptionCheck.js | 98 | ||||
-rw-r--r-- | src/intertyper.js | 2 | ||||
-rw-r--r-- | src/jsifier.js | 61 | ||||
-rw-r--r-- | src/library.js | 149 | ||||
-rw-r--r-- | src/library_browser.js | 17 | ||||
-rw-r--r-- | src/library_egl.js | 41 | ||||
-rw-r--r-- | src/library_gc.js | 2 | ||||
-rw-r--r-- | src/library_gl.js | 517 | ||||
-rw-r--r-- | src/library_glut.js | 6 | ||||
-rw-r--r-- | src/library_sdl.js | 41 | ||||
-rw-r--r-- | src/modules.js | 22 | ||||
-rw-r--r-- | src/parseTools.js | 103 | ||||
-rw-r--r-- | src/preamble.js | 221 | ||||
-rw-r--r-- | src/runtime.js | 27 | ||||
-rw-r--r-- | src/settings.js | 50 | ||||
-rw-r--r-- | src/shell.html | 2 |
18 files changed, 953 insertions, 423 deletions
diff --git a/src/analyzer.js b/src/analyzer.js index adc615fb..f9b0c5af 100644 --- a/src/analyzer.js +++ b/src/analyzer.js @@ -18,6 +18,7 @@ function recomputeLines(func) { // Handy sets var BRANCH_INVOKE = set('branch', 'invoke'); +var LABEL_ENDERS = set('branch', 'return'); var SIDE_EFFECT_CAUSERS = set('call', 'invoke', 'atomic'); var UNUNFOLDABLE = set('value', 'structvalue', 'type', 'phiparam'); @@ -88,7 +89,7 @@ function analyzer(data, sidePass) { // Internal line if (!currLabelFinished) { item.functions.slice(-1)[0].labels.slice(-1)[0].lines.push(subItem); // If this line fails, perhaps missing a label? - if (subItem.intertype === 'branch') { + if (subItem.intertype in LABEL_ENDERS) { currLabelFinished = true; } } else { @@ -684,9 +685,9 @@ function analyzer(data, sidePass) { params: [(signed && j + whole > sourceElements.length) ? signedKeepAlive : null], type: 'i32', }; - if (j == 0 && isUnsignedOp(value.op) && sourceBits < 32) { + if (j == 0 && sourceBits < 32) { // zext sign correction - result.ident = makeSignOp(result.ident, 'i' + sourceBits, 'un', 1, 1); + result.ident = makeSignOp(result.ident, 'i' + sourceBits, isUnsignedOp(value.op) ? 'un' : 're', 1, 1); } if (fraction != 0) { var other = { diff --git a/src/compiler.js b/src/compiler.js index 25c306cf..14816f1e 100644 --- a/src/compiler.js +++ b/src/compiler.js @@ -160,12 +160,6 @@ if (SAFE_HEAP >= 2) { SAFE_HEAP_LINES = set(SAFE_HEAP_LINES); // for fast checking } -if (PGO) { // by default, correct everything during PGO - CORRECT_SIGNS = CORRECT_SIGNS || 1; - CORRECT_OVERFLOWS = CORRECT_OVERFLOWS || 1; - CORRECT_ROUNDINGS = CORRECT_ROUNDINGS || 1; -} - EXPORTED_FUNCTIONS = set(EXPORTED_FUNCTIONS); EXPORTED_GLOBALS = set(EXPORTED_GLOBALS); EXCEPTION_CATCHING_WHITELIST = set(EXCEPTION_CATCHING_WHITELIST); @@ -185,13 +179,13 @@ assert(!(!NAMED_GLOBALS && BUILD_AS_SHARED_LIB)); // shared libraries must have if (phase == 'pre') { if (!MICRO_OPTS || !RELOOP || ASSERTIONS || CHECK_SIGNS || CHECK_OVERFLOWS || INIT_STACK || INIT_HEAP || - !SKIP_STACK_IN_SMALL || SAFE_HEAP || PGO || PROFILE || !DISABLE_EXCEPTION_CATCHING) { + !SKIP_STACK_IN_SMALL || SAFE_HEAP || !DISABLE_EXCEPTION_CATCHING) { print('// Note: Some Emscripten settings will significantly limit the speed of the generated code.'); } else { print('// Note: For maximum-speed code, see "Optimizing Code" on the Emscripten wiki, https://github.com/kripken/emscripten/wiki/Optimizing-Code'); } - if (DOUBLE_MODE || CORRECT_SIGNS || CORRECT_OVERFLOWS || CORRECT_ROUNDINGS) { + if (DOUBLE_MODE || CORRECT_SIGNS || CORRECT_OVERFLOWS || CORRECT_ROUNDINGS || CHECK_HEAP_ALIGN) { print('// Note: Some Emscripten settings may limit the speed of the generated code.'); } } diff --git a/src/corruptionCheck.js b/src/corruptionCheck.js new file mode 100644 index 00000000..315f5cf0 --- /dev/null +++ b/src/corruptionCheck.js @@ -0,0 +1,98 @@ + +// See settings.js, CORRUPTION_CHECK + +var CorruptionChecker = { + BUFFER_FACTOR: Math.round({{{ CORRUPTION_CHECK }}}), + + ptrs: {}, + checks: 0, + checkFrequency: 1, + + init: function() { + this.realMalloc = _malloc; + _malloc = Module['_malloc'] = this.malloc; + + this.realFree = _free; + _free = Module['_free'] = this.free; + + if (typeof _realloc != 'undefined') { + this.realRealloc = _realloc; + _realloc = Module['_realloc'] = this.realloc; + } + + __ATEXIT__.push({ func: function() { + Module.printErr('No corruption detected, ran ' + CorruptionChecker.checks + ' checks.'); + } }); + }, + malloc: function(size) { + if (size <= 0) size = 1; // malloc(0) sometimes happens - just allocate a larger area, no harm + CorruptionChecker.checkAll(); + size = (size+7)&(~7); + var allocation = CorruptionChecker.realMalloc(size*(1+2*CorruptionChecker.BUFFER_FACTOR)); + var ptr = allocation + size*CorruptionChecker.BUFFER_FACTOR; + assert(!CorruptionChecker.ptrs[ptr]); + CorruptionChecker.ptrs[ptr] = size; + CorruptionChecker.fillBuffer(allocation, size*CorruptionChecker.BUFFER_FACTOR); + CorruptionChecker.fillBuffer(allocation + size*(1+CorruptionChecker.BUFFER_FACTOR), size*CorruptionChecker.BUFFER_FACTOR); + //Module.printErr('malloc ' + size + ' ==> ' + [ptr, allocation]); + return ptr; + }, + free: function(ptr) { + if (!ptr) return; // ok to free(NULL), does nothing + CorruptionChecker.checkAll(); + var size = CorruptionChecker.ptrs[ptr]; + //Module.printErr('free ' + ptr + ' of size ' + size); + assert(size); + var allocation = ptr - size*CorruptionChecker.BUFFER_FACTOR; + //Module.printErr('free ' + ptr + ' of size ' + size + ' and allocation ' + allocation); + delete CorruptionChecker.ptrs[ptr]; + CorruptionChecker.realFree(allocation); + }, + realloc: function(ptr, newSize) { + //Module.printErr('realloc ' + ptr + ' to size ' + newSize); + if (newSize <= 0) newSize = 1; // like in malloc + if (!ptr) return CorruptionChecker.malloc(newSize); // realloc(NULL, size) forwards to malloc according to the spec + var size = CorruptionChecker.ptrs[ptr]; + assert(size); + var allocation = ptr - size*CorruptionChecker.BUFFER_FACTOR; + var newPtr = CorruptionChecker.malloc(newSize); + //Module.printErr('realloc ' + ptr + ' to size ' + newSize + ' is now ' + newPtr); + var newAllocation = newPtr + newSize*CorruptionChecker.BUFFER_FACTOR; + HEAPU8.set(HEAPU8.subarray(ptr, ptr + Math.min(size, newSize)), newPtr); + CorruptionChecker.free(ptr); + return newPtr; + }, + canary: function(x) { + return (x&127) + 10; + }, + fillBuffer: function(buffer, size) { + for (var x = buffer; x < buffer + size; x++) { + {{{ makeSetValue('x', 0, 'CorruptionChecker.canary(x)', 'i8') }}}; + } + }, + checkBuffer: function(buffer, size) { + for (var x = buffer; x < buffer + size; x++) { + if (({{{ makeGetValue('x', 0, 'i8') }}}&255) != CorruptionChecker.canary(x)) { + assert(0, 'Heap corruption detected!' + [x, buffer, size, {{{ makeGetValue('x', 0, 'i8') }}}&255, CorruptionChecker.canary(x)]); + } + } + }, + checkPtr: function(ptr) { + var size = CorruptionChecker.ptrs[ptr]; + assert(size); + var allocation = ptr - size*CorruptionChecker.BUFFER_FACTOR; + CorruptionChecker.checkBuffer(allocation, size*CorruptionChecker.BUFFER_FACTOR); + CorruptionChecker.checkBuffer(allocation + size*(1+CorruptionChecker.BUFFER_FACTOR), size*CorruptionChecker.BUFFER_FACTOR); + }, + checkAll: function(force) { + CorruptionChecker.checks++; + if (!force && CorruptionChecker.checks % CorruptionChecker.checkFrequency != 0) return; + //Module.printErr('checking for corruption ' + (CorruptionChecker.checks/CorruptionChecker.checkFrequency)); + for (var ptr in CorruptionChecker.ptrs) { + CorruptionChecker.checkPtr(ptr, false); + } + }, +}; + +CorruptionChecker.init(); + diff --git a/src/intertyper.js b/src/intertyper.js index c1a98354..6c88e765 100644 --- a/src/intertyper.js +++ b/src/intertyper.js @@ -741,10 +741,12 @@ function intertyper(data, sidePass, baseLineNums) { processItem: function(item) { item.intertype = 'atomic'; if (item.tokens[0].text == 'atomicrmw') { + if (item.tokens[1].text == 'volatile') item.tokens.splice(1, 1); item.op = item.tokens[1].text; item.tokens.splice(1, 1); } else { assert(item.tokens[0].text == 'cmpxchg') + if (item.tokens[1].text == 'volatile') item.tokens.splice(1, 1); item.op = 'cmpxchg'; } var last = getTokenIndexByText(item.tokens, ';'); diff --git a/src/jsifier.js b/src/jsifier.js index 761a5fec..ca404258 100644 --- a/src/jsifier.js +++ b/src/jsifier.js @@ -375,6 +375,7 @@ function JSify(data, functionsOnly, givenFunctions) { var ret = [item]; item.JS = 'var ' + item.ident + ';'; // Set the actual value in a postset, since it may be a global variable. We also order by dependencies there + Variables.globals[item.ident].targetIdent = item.value.ident; var value = Variables.globals[item.ident].resolvedAlias = finalizeLLVMParameter(item.value); var fix = ''; if (BUILD_AS_SHARED_LIB == 2 && !item.private_) { @@ -478,8 +479,7 @@ function JSify(data, functionsOnly, givenFunctions) { ident = '_' + ident; } var depsText = (deps ? '\n' + deps.map(addFromLibrary).filter(function(x) { return x != '' }).join('\n') : ''); - // redirected idents just need a var, but no value assigned to them - it would be unused - var contentText = isFunction ? snippet : ('var ' + ident + (redirectedIdent ? '' : '=' + snippet) + ';'); + var contentText = isFunction ? snippet : ('var ' + ident + '=' + snippet + ';'); if (ASM_JS) { var sig = LibraryManager.library[ident.substr(1) + '__sig']; if (isFunction && sig && LibraryManager.library[ident.substr(1) + '__asm']) { @@ -506,9 +506,9 @@ function JSify(data, functionsOnly, givenFunctions) { item.JS = ''; } else if (LibraryManager.library.hasOwnProperty(shortident)) { item.JS = addFromLibrary(shortident); - } else { + } else if (!LibraryManager.library.hasOwnProperty(shortident + '__inline')) { item.JS = 'var ' + item.ident + '; // stub for ' + item.ident; - if (WARN_ON_UNDEFINED_SYMBOLS) { + if (WARN_ON_UNDEFINED_SYMBOLS || ASM_JS) { // always warn on undefs in asm, since it breaks validation warn('Unresolved symbol: ' + item.ident); } } @@ -631,15 +631,6 @@ function JSify(data, functionsOnly, givenFunctions) { } } - if (PROFILE) { - func.JS += ' if (PROFILING) { ' - + 'var __parentProfilingNode__ = PROFILING_NODE; PROFILING_NODE = PROFILING_NODE.children["' + func.ident + '"]; ' - + 'if (!PROFILING_NODE) __parentProfilingNode__.children["' + func.ident + '"] = PROFILING_NODE = { time: 0, children: {}, calls: 0 };' - + 'PROFILING_NODE.calls++; ' - + 'var __profilingStartTime__ = Date.now() ' - + '}\n'; - } - if (true) { // TODO: optimize away when not needed if (CLOSURE_ANNOTATIONS) func.JS += '/** @type {number} */'; func.JS += ' var label = 0;\n'; @@ -923,7 +914,11 @@ function JSify(data, functionsOnly, givenFunctions) { case VAR_NATIVIZED: if (isNumber(item.ident)) { // Direct write to a memory address; this may be an intentional segfault, if not, it is a bug in the source - return 'throw "fault on write to ' + item.ident + '";'; + if (ASM_JS) { + return 'abort(' + item.ident + ')'; + } else { + return 'throw "fault on write to ' + item.ident + '";'; + } } return item.ident + '=' + value + ';'; // We have the actual value here break; @@ -1145,12 +1140,6 @@ function JSify(data, functionsOnly, givenFunctions) { }); makeFuncLineActor('return', function(item) { var ret = RuntimeGenerator.stackExit(item.funcData.initialStack, item.funcData.otherStackAllocations) + ';\n'; - if (PROFILE) { - ret += 'if (PROFILING) { ' - + 'PROFILING_NODE.time += Date.now() - __profilingStartTime__; ' - + 'PROFILING_NODE = __parentProfilingNode__ ' - + '}\n'; - } if (LABEL_DEBUG && functionNameFilterTest(item.funcData.ident)) { ret += "Module.print(INDENT + 'Exiting: " + item.funcData.ident + "');\n" + "INDENT = INDENT.substr(0, INDENT.length-2);\n"; @@ -1215,6 +1204,9 @@ function JSify(data, functionsOnly, givenFunctions) { switch (item.op) { case 'add': return '(tempValue=' + makeGetValue(param1, 0, type) + ',' + makeSetValue(param1, 0, 'tempValue+' + param2, type, null, null, null, null, ',') + ',tempValue)'; case 'sub': return '(tempValue=' + makeGetValue(param1, 0, type) + ',' + makeSetValue(param1, 0, 'tempValue-' + param2, type, null, null, null, null, ',') + ',tempValue)'; + case 'or': return '(tempValue=' + makeGetValue(param1, 0, type) + ',' + makeSetValue(param1, 0, 'tempValue|' + param2, type, null, null, null, null, ',') + ',tempValue)'; + case 'and': return '(tempValue=' + makeGetValue(param1, 0, type) + ',' + makeSetValue(param1, 0, 'tempValue&' + param2, type, null, null, null, null, ',') + ',tempValue)'; + case 'xor': return '(tempValue=' + makeGetValue(param1, 0, type) + ',' + makeSetValue(param1, 0, 'tempValue^' + param2, type, null, null, null, null, ',') + ',tempValue)'; case 'xchg': return '(tempValue=' + makeGetValue(param1, 0, type) + ',' + makeSetValue(param1, 0, param2, type, null, null, null, null, ',') + ',tempValue)'; case 'cmpxchg': { var param3 = finalizeLLVMParameter(item.params[2]); @@ -1237,6 +1229,15 @@ function JSify(data, functionsOnly, givenFunctions) { var impl = item.ident ? getVarImpl(item.funcData, item.ident) : VAR_EMULATED; switch (impl) { case VAR_NATIVIZED: { + if (isNumber(item.ident)) { + item.assignTo = null; + // 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 'abort(' + item.ident + ')'; + } else { + return 'throw "fault on read from ' + item.ident + '";'; + } + } return value; // We have the actual value here } case VAR_EMULATED: return makeGetValue(value, 0, item.type, 0, item.unsigned, 0, item.align); @@ -1260,7 +1261,7 @@ function JSify(data, functionsOnly, givenFunctions) { makeFuncLineActor('insertvalue', function(item) { assert(item.indexes.length == 1); // TODO: see extractvalue var ret = '(', ident; - if (item.ident === 'undef') { + if (item.ident === '0') { item.ident = 'tempValue'; ret += item.ident + ' = [' + makeEmptyStruct(item.type) + '], '; } @@ -1300,6 +1301,7 @@ function JSify(data, functionsOnly, givenFunctions) { // We cannot compile assembly. See comment in intertyper.js:'Call' assert(ident != 'asm', 'Inline assembly cannot be compiled to JavaScript!'); + ident = Variables.resolveAliasToIdent(ident); var shortident = ident.slice(1); var callIdent = LibraryManager.getRootIdent(shortident); if (callIdent) { @@ -1417,6 +1419,9 @@ function JSify(data, functionsOnly, givenFunctions) { if (ASM_JS) { assert(returnType.search(/\("'\[,/) == -1); // XXX need isFunctionType(type, out) callIdent = '(' + callIdent + ')&{{{ FTM_' + sig + ' }}}'; // the function table mask is set in emscripten.py + } else if (SAFE_DYNCALLS) { + assert(!ASM_JS, 'cannot emit safe dyncalls in asm'); + callIdent = '(tempInt=' + callIdent + ',tempInt < 0 || tempInt >= FUNCTION_TABLE.length-1 || !FUNCTION_TABLE[tempInt] ? abort("dyncall error: ' + sig + ' " + FUNCTION_TABLE_NAMES[tempInt]) : tempInt)'; } callIdent = Functions.getTable(sig) + '[' + callIdent + ']'; } @@ -1527,7 +1532,7 @@ function JSify(data, functionsOnly, givenFunctions) { print('// ASM_LIBRARY FUNCTIONS'); function fix(f) { // fix indenting to not confuse js optimizer f = f.substr(f.indexOf('f')); // remove initial spaces before 'function' - f = f.substr(0, f.lastIndexOf('\n')+1); // remove spaces and last } + f = f.substr(0, f.lastIndexOf('\n')+1); // remove spaces and last } XXX assumes function has multiple lines return f + '}'; // add unindented } to match function } print(asmLibraryFunctions.map(fix).join('\n')); @@ -1547,6 +1552,10 @@ function JSify(data, functionsOnly, givenFunctions) { // This is the main 'post' pass. Print out the generated code that we have here, together with the // rest of the output that we started to print out earlier (see comment on the // "Final shape that will be created"). + if (CORRUPTION_CHECK) { + assert(!ASM_JS); // cannot monkeypatch asm! + print(processMacros(read('corruptionCheck.js'))); + } if (PRECISE_I64_MATH && Types.preciseI64MathUsed) { print(read('long.js')); } else { @@ -1579,9 +1588,11 @@ function JSify(data, functionsOnly, givenFunctions) { var shellParts = read(shellFile).split('{{BODY}}'); print(shellParts[1]); // Print out some useful metadata (for additional optimizations later, like the eliminator) - print('// EMSCRIPTEN_GENERATED_FUNCTIONS: ' + JSON.stringify(keys(Functions.implementedFunctions).filter(function(func) { - return IGNORED_FUNCTIONS.indexOf(func.ident) < 0; - })) + '\n'); + if (EMIT_GENERATED_FUNCTIONS) { + print('// EMSCRIPTEN_GENERATED_FUNCTIONS: ' + JSON.stringify(keys(Functions.implementedFunctions).filter(function(func) { + return IGNORED_FUNCTIONS.indexOf(func.ident) < 0; + })) + '\n'); + } PassManager.serialize(); diff --git a/src/library.js b/src/library.js index ffac685b..1676a82c 100644 --- a/src/library.js +++ b/src/library.js @@ -2491,6 +2491,17 @@ LibraryManager.library = { continue; } + // TODO: Support strings like "%5c" etc. + if (format[formatIndex] === '%' && format[formatIndex+1] == 'c') { + var argPtr = {{{ makeGetValue('varargs', 'argIndex', 'void*') }}}; + argIndex += Runtime.getNativeFieldSize('void*'); + fields++; + next = get(); + {{{ makeSetValue('argPtr', 0, 'next', 'i8') }}} + formatIndex += 2; + continue; + } + // remove whitespace while (1) { next = get(); @@ -3352,14 +3363,15 @@ LibraryManager.library = { ___setErrNo(ERRNO_CODES.ECHILD); return -1; }, - perror__deps: ['puts', 'putc', 'strerror', '__errno_location'], + perror__deps: ['puts', 'fputs', 'fputc', 'strerror', '__errno_location'], perror: function(s) { // void perror(const char *s); // http://pubs.opengroup.org/onlinepubs/000095399/functions/perror.html + var stdout = {{{ makeGetValue(makeGlobalUse('_stdout'), '0', 'void*') }}}; if (s) { - _puts(s); - _putc(':'.charCodeAt(0)); - _putc(' '.charCodeAt(0)); + _fputs(s, stdout); + _fputc(':'.charCodeAt(0), stdout); + _fputc(' '.charCodeAt(0), stdout); } var errnum = {{{ makeGetValue('___errno_location()', '0', 'i32') }}}; _puts(_strerror(errnum)); @@ -4302,8 +4314,9 @@ LibraryManager.library = { ptr = ptr|0; value = value|0; num = num|0; var stop = 0, value4 = 0, stop4 = 0, unaligned = 0; stop = (ptr + num)|0; - if (num|0 >= {{{ SEEK_OPTIMAL_ALIGN_MIN }}}) { + if ((num|0) >= {{{ SEEK_OPTIMAL_ALIGN_MIN }}}) { // This is unaligned, but quite large, so work hard to get to aligned settings + value = value & 0xff; unaligned = ptr & 3; value4 = value | (value << 8) | (value << 16) | (value << 24); stop4 = stop & ~3; @@ -4514,11 +4527,16 @@ LibraryManager.library = { return 0; }, + memcmp__asm: 'true', + memcmp__sig: 'iiii', memcmp: function(p1, p2, num) { - for (var i = 0; i < num; i++) { - var v1 = {{{ makeGetValue('p1', 'i', 'i8', 0, 1) }}}; - var v2 = {{{ makeGetValue('p2', 'i', 'i8', 0, 1) }}}; - if (v1 != v2) return v1 > v2 ? 1 : -1; + p1 = p1|0; p2 = p2|0; num = num|0; + var i = 0, v1 = 0, v2 = 0; + while ((i|0) < (num|0)) { + var v1 = {{{ makeGetValueAsm('p1', 'i', 'i8', true) }}}; + var v2 = {{{ makeGetValueAsm('p2', 'i', 'i8', true) }}}; + if ((v1|0) != (v2|0)) return ((v1|0) > (v2|0) ? 1 : -1)|0; + i = (i+1)|0; } return 0; }, @@ -4619,10 +4637,8 @@ LibraryManager.library = { __strtok_state: 0, strtok__deps: ['__strtok_state', 'strtok_r'], + strtok__postset: '___strtok_state = Runtime.staticAlloc(4);', strtok: function(s, delim) { - if (!___strtok_state) { - ___strtok_state = _malloc(4); - } return _strtok_r(s, delim, ___strtok_state); }, @@ -5082,6 +5098,7 @@ LibraryManager.library = { }, __cxa_call_unexpected: function(exception) { + Module.printErr('Unexpected exception thrown, this is not properly supported - aborting'); ABORT = true; throw exception; }, @@ -6686,6 +6703,9 @@ LibraryManager.library = { pthread_mutexattr_destroy: function() {}, pthread_mutex_lock: function() {}, pthread_mutex_unlock: function() {}, + pthread_mutex_trylock: function() { + return 0; + }, pthread_cond_init: function() {}, pthread_cond_destroy: function() {}, pthread_cond_broadcast: function() {}, @@ -6728,17 +6748,27 @@ LibraryManager.library = { pthread_key_create: function(key, destructor) { if (!_pthread_key_create.keys) _pthread_key_create.keys = {}; - _pthread_key_create.keys[key] = null; + // values start at 0 + _pthread_key_create.keys[key] = 0; }, pthread_getspecific: function(key) { - return _pthread_key_create.keys[key]; + return _pthread_key_create.keys[key] || 0; }, pthread_setspecific: function(key, value) { _pthread_key_create.keys[key] = value; }, + pthread_key_delete: ['$ERRNO_CODES'], + pthread_key_delete: function(key) { + if (_pthread_key_create.keys[key]) { + delete _pthread_key_create.keys[key]; + return 0; + } + return ERRNO_CODES.EINVAL; + }, + pthread_cleanup_push: function(routine, arg) { __ATEXIT__.push({ func: function() { Runtime.dynCall('vi', routine, [arg]) } }) _pthread_cleanup_push.level = __ATEXIT__.length; @@ -7243,23 +7273,56 @@ LibraryManager.library = { }, select: function(nfds, readfds, writefds, exceptfds, timeout) { - // only readfds are supported, not writefds or exceptfds + // readfds are supported, + // writefds checks socket open status + // exceptfds not supported // timeout is always 0 - fully async - assert(!writefds && !exceptfds); - var ret = 0; - var l = {{{ makeGetValue('readfds', 0, 'i32') }}}; - var h = {{{ makeGetValue('readfds', 4, 'i32') }}}; - nfds = Math.min(64, nfds); // fd sets have 64 bits - for (var fd = 0; fd < nfds; fd++) { - var bit = fd % 32, int = fd < 32 ? l : h; - if (int & (1 << bit)) { - // index is in the set, check if it is ready for read - var info = Sockets.fds[fd]; - if (!info) continue; - if (info.hasData()) ret++; + assert(!exceptfds); + + function canRead(info) { + // make sure hasData exists. + // we do create it when the socket is connected, + // but other implementations may create it lazily + return info.hasData && info.hasData(); + } + + function canWrite(info) { + // make sure socket exists. + // we do create it when the socket is connected, + // but other implementations may create it lazily + return info.socket && (info.socket.readyState == info.socket.OPEN); + } + + function checkfds(nfds, fds, can) { + if (!fds) return 0; + + var bitsSet = 0; + var dstLow = 0; + var dstHigh = 0; + var srcLow = {{{ makeGetValue('fds', 0, 'i32') }}}; + var srcHigh = {{{ makeGetValue('fds', 4, 'i32') }}}; + nfds = Math.min(64, nfds); // fd sets have 64 bits + + for (var fd = 0; fd < nfds; fd++) { + var mask = 1 << (fd % 32), int = fd < 32 ? srcLow : srcHigh; + if (int & mask) { + // index is in the set, check if it is ready for read + var info = Sockets.fds[fd]; + if (info && can(info)) { + // set bit + fd < 32 ? (dstLow = dstLow | mask) : (dstHigh = dstHigh | mask); + bitsSet++; + } + } } + + {{{ makeSetValue('fds', 0, 'dstLow', 'i32') }}}; + {{{ makeSetValue('fds', 4, 'dstHigh', 'i32') }}}; + return bitsSet; } - return ret; + + return checkfds(nfds, readfds, canRead) + + checkfds(nfds, writefds, canWrite); }, // pty.h @@ -7305,36 +7368,6 @@ LibraryManager.library = { emscripten_random: function() { return Math.random(); }, - - $Profiling: { - max_: 0, - times: null, - invalid: 0, - dump: function() { - if (Profiling.invalid) { - Module.printErr('Invalid # of calls to Profiling begin and end!'); - return; - } - Module.printErr('Profiling data:') - for (var i = 0; i < Profiling.max_; i++) { - Module.printErr('Block ' + i + ': ' + Profiling.times[i]); - } - } - }, - EMSCRIPTEN_PROFILE_INIT__deps: ['$Profiling'], - EMSCRIPTEN_PROFILE_INIT: function(max_) { - Profiling.max_ = max_; - Profiling.times = new Array(max_); - for (var i = 0; i < max_; i++) Profiling.times[i] = 0; - }, - EMSCRIPTEN_PROFILE_BEGIN__inline: function(id) { - return 'Profiling.times[' + id + '] -= Date.now();' - + 'Profiling.invalid++;' - }, - EMSCRIPTEN_PROFILE_END__inline: function(id) { - return 'Profiling.times[' + id + '] += Date.now();' - + 'Profiling.invalid--;' - } }; function autoAddDeps(object, name) { diff --git a/src/library_browser.js b/src/library_browser.js index 6b1e956d..5b19a360 100644 --- a/src/library_browser.js +++ b/src/library_browser.js @@ -68,6 +68,7 @@ mergeInto(LibraryManager.library, { function getMimetype(name) { return { 'jpg': 'image/jpeg', + 'jpeg': 'image/jpeg', 'png': 'image/png', 'bmp': 'image/bmp', 'ogg': 'audio/ogg', @@ -81,7 +82,7 @@ mergeInto(LibraryManager.library, { var imagePlugin = {}; imagePlugin['canHandle'] = function(name) { - return !Module.noImageDecoding && name.substr(-4) in { '.jpg': 1, '.png': 1, '.bmp': 1 }; + return !Module.noImageDecoding && /\.(jpg|jpeg|png|bmp)$/.exec(name); }; imagePlugin['handle'] = function(byteArray, name, onload, onerror) { var b = null; @@ -200,8 +201,18 @@ mergeInto(LibraryManager.library, { return null; } #endif + var ctx; try { - var ctx = canvas.getContext(useWebGL ? 'experimental-webgl' : '2d'); + if (useWebGL) { + ctx = canvas.getContext('experimental-webgl', { + alpha: false, +#if GL_TESTING + preserveDrawingBuffer: true +#endif + }); + } else { + ctx = canvas.getContext('2d'); + } if (!ctx) throw ':('; } catch (e) { Module.print('Could not create canvas - ' + e); @@ -262,7 +273,7 @@ mergeInto(LibraryManager.library, { } return ctx; }, - + destroyContext: function(canvas, useWebGL, setInModule) {}, requestFullScreen: function() { var canvas = Module['canvas']; function fullScreenChange() { diff --git a/src/library_egl.js b/src/library_egl.js index a9eb37dd..271ea29e 100644 --- a/src/library_egl.js +++ b/src/library_egl.js @@ -83,6 +83,17 @@ var LibraryEGL = { 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.setErrorCode(0x3000 /* EGL_SUCCESS */); + return 1; + }, + // EGLAPI EGLBoolean EGLAPIENTRY eglGetConfigs(EGLDisplay dpy, EGLConfig *configs, EGLint config_size, EGLint *num_config); eglGetConfigs: function(display, configs, config_size, numConfigs) { return EGL.chooseConfig(display, 0, configs, config_size, numConfigs); @@ -225,6 +236,20 @@ var LibraryEGL = { return 62006; /* Magic ID for Emscripten 'default surface' */ }, + // EGLAPI EGLBoolean EGLAPIENTRY eglDestroySurface(EGLDisplay display, EGLSurface surface); + eglDestroySurface: function(display, surface) { + if (display != 62000 /* Magic ID for Emscripten 'default display' */) { + EGL.setErrorCode(0x3008 /* EGL_BAD_DISPLAY */); + return 0; + } + if (surface != 62006 /* Magic ID for the only EGLSurface supported by Emscripten */) { + EGL.setErrorCode(0x300D /* EGL_BAD_SURFACE */); + return 1; + } + EGL.setErrorCode(0x3000 /* EGL_SUCCESS */); + return 1; /* Magic ID for Emscripten 'default surface' */ + }, + eglCreateContext__deps: ['glutCreateWindow', '$GL'], // EGLAPI EGLContext EGLAPIENTRY eglCreateContext(EGLDisplay dpy, EGLConfig config, EGLContext share_context, const EGLint *attrib_list); @@ -234,7 +259,21 @@ var LibraryEGL = { return 0; } - _glutCreateWindow(); + EGL.windowID = _glutCreateWindow(); + EGL.setErrorCode(0x3000 /* EGL_SUCCESS */); + return 62004; // Magic ID for Emscripten EGLContext + }, + + eglDestroyContext__deps: ['glutDestroyWindow', '$GL'], + + // EGLAPI EGLBoolean EGLAPIENTRY eglDestroyContext(EGLDisplay dpy, EGLContext context); + eglDestroyContext: function(display, context) { + if (display != 62000 /* Magic ID for Emscripten 'default display' */) { + EGL.setErrorCode(0x3008 /* EGL_BAD_DISPLAY */); + return 0; + } + + _glutDestroyWindow(EGL.windowID); EGL.setErrorCode(0x3000 /* EGL_SUCCESS */); return 62004; // Magic ID for Emscripten EGLContext }, diff --git a/src/library_gc.js b/src/library_gc.js index 083019ca..f6db74d8 100644 --- a/src/library_gc.js +++ b/src/library_gc.js @@ -4,7 +4,7 @@ if (GC_SUPPORT) { EXPORTED_FUNCTIONS['_realloc'] = 1; var LibraryGC = { - $GC__deps: ['sbrk', 'realloc'], + $GC__deps: ['sbrk', 'realloc', 'calloc'], $GC: { ALLOCATIONS_TO_GC: 1*1024*1024, diff --git a/src/library_gl.js b/src/library_gl.js index 8b29e4e2..4977d2e9 100644 --- a/src/library_gl.js +++ b/src/library_gl.js @@ -19,6 +19,28 @@ var LibraryGL = { uniforms: [], shaders: [], +#if FULL_ES2 + clientBuffers: [], + enabledClientBuffers: [], +#endif + currArrayBuffer: 0, + currElementArrayBuffer: 0, + + byteSizeByTypeRoot: 0x1400, // GL_BYTE + byteSizeByType: [ + 1, // GL_BYTE + 1, // GL_UNSIGNED_BYTE + 2, // GL_SHORT + 2, // GL_UNSIGNED_SHORT + 4, // GL_INT + 4, // GL_UNSIGNED_INT + 4, // GL_FLOAT + 2, // GL_2_BYTES + 3, // GL_3_BYTES + 4, // GL_4_BYTES + 8 // GL_DOUBLE + ], + uniformTable: {}, // name => uniform ID. the uID must be identical until relinking, cannot create a new uID each call to glGetUniformLocation packAlignment: 4, // default alignment is 4 bytes @@ -176,12 +198,48 @@ var LibraryGL = { } }, +#if FULL_ES2 + calcBufLength: function(size, type, stride, count) { + if (stride > 0) { + return count * stride; // XXXvlad this is not exactly correct I don't think + } + var typeSize = GL.byteSizeByType[type - GL.byteSizeByTypeRoot]; + return size * typeSize * count; + }, + + preDrawHandleClientVertexAttribBindings: function(count) { + GL.resetBufferBinding = false; + for (var i = 0; i < GL.maxVertexAttribs; ++i) { + if (!GL.enabledClientBuffers[i] || !GL.clientBuffers[i]) continue; + + GL.resetBufferBinding = true; + + var cb = GL.clientBuffers[i]; + + var buf = Module.ctx.createBuffer(); + Module.ctx.bindBuffer(Module.ctx.ARRAY_BUFFER, buf); + Module.ctx.bufferData(Module.ctx.ARRAY_BUFFER, + HEAPU8.subarray(cb.ptr, cb.ptr + GL.calcBufLength(cb.size, cb.type, cb.stride, count)), + Module.ctx.DYNAMIC_DRAW); + Module.ctx.vertexAttribPointer(i, cb.size, cb.type, cb.normalized, cb.stride, 0); + } + }, + + postDrawHandleClientVertexAttribBindings: function() { + if (GL.resetBufferBinding) { + Module.ctx.bindBuffer(Module.ctx.ARRAY_BUFFER, GL.buffers[GL.currArrayBuffer]); + } + }, +#endif + initExtensions: function() { if (GL.initExtensions.done) return; GL.initExtensions.done = true; |