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