diff --git a/src/library_async.js b/src/library_async.js index 97cfb845d..e05a303b9 100644 --- a/src/library_async.js +++ b/src/library_async.js @@ -243,7 +243,6 @@ mergeInto(LibraryManager.library, { // copy the stack back in and resume HEAP32.set(stack, EMTSTACKTOP>>2); #if ASSERTIONS - EmterpreterAsync.setState(0); // set it to 0 just so stackSave is ok to run assert(stacktop === asm.stackSave()); // nothing should have modified the stack meanwhile #endif EmterpreterAsync.setState(2); diff --git a/tests/emterpreter_async_bad.cpp b/tests/emterpreter_async_bad.cpp index 30991fa8e..0ef965190 100644 --- a/tests/emterpreter_async_bad.cpp +++ b/tests/emterpreter_async_bad.cpp @@ -33,7 +33,13 @@ int main() { EM_ASM({ window.onerror = function(err) { assert(err.toString().indexOf('This error happened during an emterpreter-async save or load of the stack') > 0, 'expect good error message'); - Module._finish(1); + // manually REPORT_RESULT; we can't call back into native code at this point, assertions would trigger + xhr = new XMLHttpRequest(); + xhr.open("GET", "http://localhost:8888/report_result?1"); + xhr.onload = xhr.onerror = function() { + window.close(); + }; + xhr.send(); }; }); diff --git a/tools/emterpretify.py b/tools/emterpretify.py index 1b0451898..b6c62ab88 100755 --- a/tools/emterpretify.py +++ b/tools/emterpretify.py @@ -57,6 +57,7 @@ sys.argv = filter(handle_arg, sys.argv) BLACKLIST = set(['_malloc', '_free', '_memcpy', '_memmove', '_memset', 'copyTempDouble', 'copyTempFloat', '_strlen', 'stackAlloc', 'setThrew', 'stackRestore', 'setTempRet0', 'getTempRet0', 'stackSave', 'runPostSets', '_emscripten_autodebug_double', '_emscripten_autodebug_float', '_emscripten_autodebug_i8', '_emscripten_autodebug_i16', '_emscripten_autodebug_i32', '_emscripten_autodebug_i64', '_strncpy', '_strcpy', '_strcat', '_saveSetjmp', '_testSetjmp', '_emscripten_replace_memory', '_bitshift64Shl', '_bitshift64Ashr', '_bitshift64Lshr', 'setAsyncState', 'emtStackSave']) WHITELIST = [] +YIELDLIST = ['stackSave'] # functions which are ok to run while doing a sleep_with_yield. SYNC_FUNCS = set(['_emscripten_sleep', '_emscripten_sleep_with_yield', '_emscripten_wget_data', '_emscripten_idb_load', '_emscripten_idb_store', '_emscripten_idb_delete']) @@ -696,8 +697,7 @@ function emterpret%s(pc) { '' if not zero else '_z', 'sp = 0, ' if not zero else '', '' if not ASYNC and not MEMORY_SAFE else 'var ld = +0;', - '' if not ASYNC else 'HEAP32[EMTSTACKTOP>>2] = pc;\n if ((asyncState|0) == 1) asyncState = 0;\n', # other code running between a save and resume can see state 1, just reset - # (optimally we should flip it to 0 at the bottom of the saving stack) + '' if not ASYNC else 'HEAP32[EMTSTACKTOP>>2] = pc;\n', push_stacktop(zero), ROPCODES['FUNC'], (''' EMTSTACKTOP = EMTSTACKTOP + (lx ''' + (' + 1 ' if ASYNC else '') + '''<< 3) | 0; @@ -831,7 +831,7 @@ if __name__ == '__main__': external_emterpreted_funcs = filter(lambda func: func in tabled_funcs or func in exported_funcs or func in reachable_funcs, emterpreted_funcs) # process functions, generating bytecode - shared.Building.js_optimizer(infile, ['emterpretify'], extra_info={ 'emterpretedFuncs': list(emterpreted_funcs), 'externalEmterpretedFuncs': list(external_emterpreted_funcs), 'opcodes': OPCODES, 'ropcodes': ROPCODES, 'ASYNC': ASYNC, 'PROFILING': PROFILING, 'ASSERTIONS': ASSERTIONS }, output_filename=temp, just_concat=True) + shared.Building.js_optimizer(infile, ['emterpretify'], extra_info={ 'emterpretedFuncs': list(emterpreted_funcs), 'externalEmterpretedFuncs': list(external_emterpreted_funcs), 'opcodes': OPCODES, 'ropcodes': ROPCODES, 'ASYNC': ASYNC, 'PROFILING': PROFILING, 'ASSERTIONS': ASSERTIONS, 'yieldFuncs': YIELDLIST }, output_filename=temp, just_concat=True) # load the module and modify it asm = asm_module.AsmModule(temp) diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js index 7149aa9b6..0ba9dca48 100644 --- a/tools/js-optimizer.js +++ b/tools/js-optimizer.js @@ -6156,6 +6156,7 @@ function emterpretify(ast) { var ASYNC = extraInfo.ASYNC; var PROFILING = extraInfo.PROFILING; var ASSERTIONS = extraInfo.ASSERTIONS; + var yieldFuncs = set(extraInfo.yieldFuncs); var RELATIVE_BRANCHES = set('BR', 'BRT', 'BRF'); var ABSOLUTE_BRANCHES = set('BRA', 'BRTA', 'BRFA'); @@ -7438,7 +7439,7 @@ function emterpretify(ast) { if (ignore) { // we are not emterpreting this function - if (ASYNC && ASSERTIONS && !/^dynCall_/.test(func[1])) { + if (ASYNC && ASSERTIONS && !/^dynCall_/.test(func[1]) && !(func[1] in yieldFuncs)) { // we need to be careful to never enter non-emterpreted code while doing an async save/restore, // which is what happens if non-emterpreted code is on the stack while we attempt to save. // note that we special-case dynCall, which *can* be on the stack, they are just bridges; what