diff --git a/AUTHORS b/AUTHORS index 69dbcd2aea9d3..2979ffd95efb0 100644 --- a/AUTHORS +++ b/AUTHORS @@ -150,4 +150,5 @@ a license to everyone to use it as detailed in LICENSE.) * Sebastien Ronsse <sronsse@gmail.com> * Glenn R. Wichman <gwichman@zynga.com> * Hamish Willee <hamishwillee@gmail.com> (copyright owned by Mozilla Foundation) - +* Sylvain Chevalier <sylvain.chevalier@gmail.com> +* Nathan Ross <nross.se@gmail.com> diff --git a/emscripten-version.txt b/emscripten-version.txt index 8b65a411073b7..db98d22c01c7e 100644 --- a/emscripten-version.txt +++ b/emscripten-version.txt @@ -1,2 +1,2 @@ -1.21.7 +1.21.8 diff --git a/src/deps_info.json b/src/deps_info.json index a2b24698ff367..7e3ab1f02dbbf 100644 --- a/src/deps_info.json +++ b/src/deps_info.json @@ -31,6 +31,7 @@ "SDL_GL_GetProcAddress": ["emscripten_GetProcAddress"], "eglGetProcAddress": ["emscripten_GetProcAddress"], "glfwGetProcAddress": ["emscripten_GetProcAddress"], - "emscripten_GetProcAddress": ["strstr"] + "emscripten_GetProcAddress": ["strstr"], + "__cxa_begin_catch": ["__cxa_can_catch", "__cxa_is_pointer_type"] } diff --git a/src/library.js b/src/library.js index 6e4c34a484f32..4828363d5cad9 100644 --- a/src/library.js +++ b/src/library.js @@ -3960,7 +3960,7 @@ LibraryManager.library = { // Call destructor if one is registered then clear it. var ptr = ___cxa_caught_exceptions.pop(); if (ptr) { - header = ptr - ___cxa_exception_header_size; + var header = ptr - ___cxa_exception_header_size; var destructor = {{{ makeGetValue('header', 4, 'void*') }}}; if (destructor) { Runtime.dynCall('vi', destructor, [ptr]); @@ -3993,25 +3993,6 @@ LibraryManager.library = { __gxx_personality_v0: function() { }, - __cxa_is_number_type: function(type) { - var isNumber = false; - try { if (type == {{{ makeGlobalUse('__ZTIi') }}}) isNumber = true } catch(e){} - try { if (type == {{{ makeGlobalUse('__ZTIj') }}}) isNumber = true } catch(e){} - try { if (type == {{{ makeGlobalUse('__ZTIl') }}}) isNumber = true } catch(e){} - try { if (type == {{{ makeGlobalUse('__ZTIm') }}}) isNumber = true } catch(e){} - try { if (type == {{{ makeGlobalUse('__ZTIx') }}}) isNumber = true } catch(e){} - try { if (type == {{{ makeGlobalUse('__ZTIy') }}}) isNumber = true } catch(e){} - try { if (type == {{{ makeGlobalUse('__ZTIf') }}}) isNumber = true } catch(e){} - try { if (type == {{{ makeGlobalUse('__ZTId') }}}) isNumber = true } catch(e){} - try { if (type == {{{ makeGlobalUse('__ZTIe') }}}) isNumber = true } catch(e){} - try { if (type == {{{ makeGlobalUse('__ZTIc') }}}) isNumber = true } catch(e){} - try { if (type == {{{ makeGlobalUse('__ZTIa') }}}) isNumber = true } catch(e){} - try { if (type == {{{ makeGlobalUse('__ZTIh') }}}) isNumber = true } catch(e){} - try { if (type == {{{ makeGlobalUse('__ZTIs') }}}) isNumber = true } catch(e){} - try { if (type == {{{ makeGlobalUse('__ZTIt') }}}) isNumber = true } catch(e){} - return isNumber; - }, - // Finds a suitable catch clause for when an exception is thrown. // In normal compilers, this functionality is handled by the C++ // 'personality' routine. This is passed a fairly complex structure @@ -4022,17 +4003,18 @@ LibraryManager.library = { // functionality boils down to picking a suitable 'catch' block. // We'll do that here, instead, to keep things simpler. - __cxa_find_matching_catch__deps: ['__cxa_does_inherit', '__cxa_is_number_type', '__resumeException', '__cxa_last_thrown_exception', '__cxa_exception_header_size'], + __cxa_find_matching_catch__deps: ['__resumeException', '__cxa_last_thrown_exception', '__cxa_exception_header_size'], __cxa_find_matching_catch: function(thrown, throwntype) { if (thrown == -1) thrown = ___cxa_last_thrown_exception; - header = thrown - ___cxa_exception_header_size; + var header = thrown - ___cxa_exception_header_size; if (throwntype == -1) throwntype = {{{ makeGetValue('header', 0, 'void*') }}}; var typeArray = Array.prototype.slice.call(arguments, 2); // If throwntype is a pointer, this means a pointer has been // thrown. When a pointer is thrown, actually what's thrown // is a pointer to the pointer. We'll dereference it. - if (throwntype != 0 && !___cxa_is_number_type(throwntype)) { + var thrownPtr = thrown; + if (throwntype != 0 && Module['___cxa_is_pointer_type'](throwntype)) { var throwntypeInfoAddr= {{{ makeGetValue('throwntype', '0', '*') }}} - {{{ Runtime.QUANTUM_SIZE*2 }}}; var throwntypeInfo= {{{ makeGetValue('throwntypeInfoAddr', '0', '*') }}}; if (throwntypeInfo == 0) @@ -4043,8 +4025,9 @@ LibraryManager.library = { // type of the thrown object. Find one which matches, and // return the type of the catch block which should be called. for (var i = 0; i < typeArray.length; i++) { - if (___cxa_does_inherit(typeArray[i], throwntype, thrown)) + if (typeArray[i] && Module['___cxa_can_catch'](typeArray[i], throwntype, thrown)) { // XXX thrown should be an out ptr {{{ makeStructuralReturn(['thrown', 'typeArray[i]']) }}}; + } } // Shouldn't happen unless we have bogus data in typeArray // or encounter a type for which emscripten doesn't have suitable @@ -4061,78 +4044,6 @@ LibraryManager.library = { {{{ makeThrow('ptr') }}} }, - // Recursively walks up the base types of 'possibilityType' - // to see if any of them match 'definiteType'. - __cxa_does_inherit__deps: ['__cxa_is_number_type'], - __cxa_does_inherit: function(definiteType, possibilityType, possibility) { - if (possibility == 0) return false; - if (possibilityType == 0 || possibilityType == definiteType) - return true; - var possibility_type_info; - if (___cxa_is_number_type(possibilityType)) { - possibility_type_info = possibilityType; - } else { - var possibility_type_infoAddr = {{{ makeGetValue('possibilityType', '0', '*') }}} - {{{ Runtime.QUANTUM_SIZE*2 }}}; - possibility_type_info = {{{ makeGetValue('possibility_type_infoAddr', '0', '*') }}}; - } - switch (possibility_type_info) { - case 0: // possibility is a pointer - // See if definite type is a pointer - var definite_type_infoAddr = {{{ makeGetValue('definiteType', '0', '*') }}} - {{{ Runtime.QUANTUM_SIZE*2 }}}; - var definite_type_info = {{{ makeGetValue('definite_type_infoAddr', '0', '*') }}}; - if (definite_type_info == 0) { - // Also a pointer; compare base types of pointers - var defPointerBaseAddr = definiteType+{{{ Runtime.QUANTUM_SIZE*2 }}}; - var defPointerBaseType = {{{ makeGetValue('defPointerBaseAddr', '0', '*') }}}; - var possPointerBaseAddr = possibilityType+{{{ Runtime.QUANTUM_SIZE*2 }}}; - var possPointerBaseType = {{{ makeGetValue('possPointerBaseAddr', '0', '*') }}}; - return ___cxa_does_inherit(defPointerBaseType, possPointerBaseType, possibility); - } else - return false; // one pointer and one non-pointer - case 1: // class with no base class - return false; - case 2: // class with base class - var parentTypeAddr = possibilityType + {{{ Runtime.QUANTUM_SIZE*2 }}}; - var parentType = {{{ makeGetValue('parentTypeAddr', '0', '*') }}}; - return ___cxa_does_inherit(definiteType, parentType, possibility); - default: - return false; // some unencountered type - } - }, - - _ZNKSt9exception4whatEv__deps: ['malloc'], - _ZNKSt9exception4whatEv: function() { - if (!__ZNKSt9exception4whatEv.buffer) { - var name = "std::exception"; - __ZNKSt9exception4whatEv.buffer = _malloc(name.length + 1); - writeStringToMemory(name, __ZNKSt9exception4whatEv.buffer); - } - return __ZNKSt9exception4whatEv.buffer; - }, - - // RTTI hacks for exception handling, defining type_infos for common types. - // The values are dummies. We simply use the addresses of these statically - // allocated variables as unique identifiers. - _ZTIb: [0], // bool - _ZTIi: [0], // int - _ZTIj: [0], // unsigned int - _ZTIl: [0], // long - _ZTIm: [0], // unsigned long - _ZTIx: [0], // long long - _ZTIy: [0], // unsigned long long - _ZTIf: [0], // float - _ZTId: [0], // double - _ZTIe: [0], // long double - _ZTIc: [0], // char - _ZTIa: [0], // signed char - _ZTIh: [0], // unsigned char - _ZTIs: [0], // short - _ZTIt: [0], // unsigned short - _ZTIv: [0], // void - _ZTIPv: [0], // void* - - _ZTISt9exception: 'allocate([allocate([1,0,0,0,0,0,0], "i8", ALLOC_STATIC)+8, 0], "i32", ALLOC_STATIC)', // typeinfo for std::exception - llvm_uadd_with_overflow_i8: function(x, y) { x = x & 0xff; y = y & 0xff; diff --git a/src/postamble.js b/src/postamble.js index 49e352f40201a..cbf6450f5c376 100644 --- a/src/postamble.js +++ b/src/postamble.js @@ -175,7 +175,14 @@ function exit(status) { exitRuntime(); if (ENVIRONMENT_IS_NODE) { - process['exit'](status); + // Work around a node.js bug where stdout buffer is not flushed at process exit: + // Instead of process.exit() directly, wait for stdout flush event. + // See https://github.com/joyent/node/issues/1669 and https://github.com/kripken/emscripten/issues/2582 + // Workaround is based on https://github.com/RReverser/acorn/commit/50ab143cecc9ed71a2d66f78b4aec3bb2e9844f6 + process.stdout.once('drain', function () { + process['exit'](status); + }); + console.log(' '); // Make sure to print something to force the drain event to occur, in case the stdout buffer was empty. } else if (ENVIRONMENT_IS_SHELL && typeof quit === 'function') { quit(status); } else { diff --git a/system/lib/libcxxabi/src/private_typeinfo.cpp b/system/lib/libcxxabi/src/private_typeinfo.cpp index 52326a3e76c2f..8f873aec93c45 100644 --- a/system/lib/libcxxabi/src/private_typeinfo.cpp +++ b/system/lib/libcxxabi/src/private_typeinfo.cpp @@ -1159,4 +1159,24 @@ __base_class_type_info::search_below_dst(__dynamic_cast_info* info, #pragma GCC visibility pop +#ifdef __EMSCRIPTEN__ +extern "C" { + +int __cxa_can_catch(__shim_type_info* catchType, __shim_type_info* excpType, void *thrown) { + //std::type_info *t1 = static_cast<std::type_info*>(catchType); + //std::type_info *t2 = static_cast<std::type_info*>(excpType); + //printf("can %s catch %s (%p)?\n", t1->name(), t2->name(), thrown); + + void *tempPtr = thrown; // XXX + return catchType->can_catch(excpType, tempPtr); +} + +int __cxa_is_pointer_type(__shim_type_info* type) { + return !!dynamic_cast<__pointer_type_info*>(type); +} + +} +#endif // __EMSCRIPTEN__ + + } // __cxxabiv1 diff --git a/tests/core/test_llvm_used.in b/tests/core/test_llvm_used.in index b3c9f10ef82ef..b95a66e8aa187 100644 --- a/tests/core/test_llvm_used.in +++ b/tests/core/test_llvm_used.in @@ -2,6 +2,7 @@ #include <emscripten.h> extern "C" { + __attribute__((annotate("hello attribute world"))) EMSCRIPTEN_KEEPALIVE void foobar(int x) { printf("Worked! %d\n", x); } diff --git a/tests/test_core.py b/tests/test_core.py index 1dd0222ae9e7f..7c9b18bfce2a3 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -1324,6 +1324,7 @@ def test_exceptions_2(self): def test_exceptions_3(self): if self.emcc_args is None: return self.skip('need emcc to add in libcxx properly') if self.run_name == 'asm2x86': return self.skip('TODO') + if os.environ.get('EMCC_FAST_COMPILER') == '0': return self.skip('needs fastcomp') Settings.DISABLE_EXCEPTION_CATCHING = 0 @@ -1452,9 +1453,10 @@ def test_exceptions_uncaught(self): def test_exceptions_typed(self): if self.emcc_args is None: return self.skip('requires emcc') + if os.environ.get('EMCC_FAST_COMPILER') == '0': return self.skip('needs fastcomp') Settings.DISABLE_EXCEPTION_CATCHING = 0 - Settings.SAFE_HEAP = 0 # Throwing null will cause an ignorable null pointer access. + self.emcc_args += ['-s', 'SAFE_HEAP=0'] # Throwing null will cause an ignorable null pointer access. test_path = path_from_root('tests', 'core', 'test_exceptions_typed') src, output = (test_path + s for s in ('.in', '.out')) diff --git a/tests/test_other.py b/tests/test_other.py index f05a8249fdeec..999ca92b8e11b 100644 --- a/tests/test_other.py +++ b/tests/test_other.py @@ -3723,3 +3723,30 @@ def test_create_readonly(self): Failed to open file for writing: /tmp/file; errno=13; Permission denied ''', run_js('a.out.js')) + def test_embed_file_large(self): + # If such long files are encoded on one line, + # they overflow the interpreter's limit + large_size = int(40e6) + open('large.txt', 'w').write('x' * large_size) + open('src.cpp', 'w').write(r''' + #include <stdio.h> + #include <unistd.h> + int main() + { + FILE* fp = fopen("large.txt", "r"); + if (fp) { + printf("%d\n", fp); + fseek(fp, 0L, SEEK_END); + printf("%d\n", ftell(fp)); + } else { + printf("failed to open large file.txt\n"); + } + return 0; + } + ''') + Popen([PYTHON, EMCC, 'src.cpp', '--embed-file', 'large.txt']).communicate() + for engine in JS_ENGINES: + if engine == V8_ENGINE: continue # ooms + print engine + self.assertContained('4\n' + str(large_size) + '\n', run_js('a.out.js', engine=engine)) + diff --git a/tools/file_packager.py b/tools/file_packager.py index 0ab6851132421..1a5c14259e3a4 100644 --- a/tools/file_packager.py +++ b/tools/file_packager.py @@ -414,19 +414,17 @@ def was_seen(name): if file_['mode'] == 'embed': # Embed data = map(ord, open(file_['srcpath'], 'rb').read()) - if not data: - str_data = '[]' - else: - str_data = '' + code += '''fileData%d = [];\n''' % counter + if data: + parts = [] chunk_size = 10240 - while len(data) > 0: - chunk = data[:chunk_size] - data = data[chunk_size:] - if not str_data: - str_data = str(chunk) - else: - str_data += '.concat(' + str(chunk) + ')' - code += '''Module['FS_createDataFile']('%s', '%s', %s, true, true);\n''' % (dirname, basename, str_data) + start = 0 + while start < len(data): + parts.append('''fileData%d.push.apply(fileData%d, %s);\n''' % (counter, counter, str(data[start:start+chunk_size]))) + start += chunk_size + code += ''.join(parts) + code += '''Module['FS_createDataFile']('%s', '%s', fileData%d, true, true);\n''' % (dirname, basename, counter) + counter += 1 elif file_['mode'] == 'preload': # Preload varname = 'filePreload%d' % counter