aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/library.js82
-rw-r--r--tests/exceptions/output.txt54
-rw-r--r--tests/exceptions/typed.cpp91
-rw-r--r--tests/runner.py9
4 files changed, 228 insertions, 8 deletions
diff --git a/src/library.js b/src/library.js
index cd204d95..88feb66c 100644
--- a/src/library.js
+++ b/src/library.js
@@ -3992,25 +3992,73 @@ LibraryManager.library = {
return 1;
},
- // Exceptions - minimal support, only (...) for now (no actual exception objects can be caught)
+ // Exceptions
__cxa_allocate_exception: function(size) {
- return _malloc(size); // warning: leaked
+ return _malloc(size);
},
- __cxa_throw: function(ptr, data, dunno) {
+ __cxa_free_exception: function(ptr) {
+ return _free(ptr);
+ },
+ __cxa_throw__deps: ['llvm_eh_exception'],
+ __cxa_throw: function(ptr, type, destructor) {
#if EXCEPTION_DEBUG
- print('Compiled code throwing an exception, ' + [ptr,data,dunno] + ', at ' + new Error().stack);
+ print('Compiled code throwing an exception, ' + [ptr,type,destructor] + ', at ' + new Error().stack);
#endif
+ {{{ makeSetValue('_llvm_eh_exception.buf', '0', 'ptr', 'void*') }}}
+ {{{ makeSetValue('_llvm_eh_exception.buf', '4', 'type', 'void*') }}}
+ {{{ makeSetValue('_llvm_eh_exception.buf', '8', 'destructor', 'void*') }}}
throw ptr;
},
+ __cxa_rethrow__deps: ['llvm_eh_exception', '__cxa_end_catch'],
+ __cxa_rethrow: function() {
+ ___cxa_end_catch.rethrown = true;
+ throw {{{ makeGetValue('_llvm_eh_exception.buf', '0', 'void*') }}};
+ },
+ llvm_eh_exception__postset: '_llvm_eh_exception.buf = allocate(12, "void*", ALLOC_STATIC);',
llvm_eh_exception: function() {
- return 'code-generated exception: ' + (new Error().stack);
+ return {{{ makeGetValue('_llvm_eh_exception.buf', '0', 'void*') }}};
},
- llvm_eh_selector: function(exception, personality, num) {
+ llvm_eh_selector__jsargs: true,
+ llvm_eh_selector: function(unused_exception_value, personality/*, varargs*/) {
+ var type = {{{ makeGetValue('_llvm_eh_exception.buf', '4', 'void*') }}}
+ for (var i = 2; i < arguments.length; i++) {
+ if (arguments[i] == type) return type;
+ }
return 0;
},
+ llvm_eh_typeid_for: function(type) {
+ return type;
+ },
+ _Unwind_Resume_or_Rethrow: function(ptr) {
+ throw ptr;
+ },
__cxa_begin_catch: function(ptr) {
+ return ptr;
},
- __cxa_end_catch: function(ptr) {
+ __cxa_end_catch__deps: ['llvm_eh_exception', '__cxa_free_exception'],
+ __cxa_end_catch: function() {
+ if (___cxa_end_catch.rethrown) {
+ ___cxa_end_catch.rethrown = false;
+ return;
+ }
+ // Clear state flag.
+ __THREW__ = false;
+ // Free ptr if it isn't null.
+ if ({{{ makeGetValue('_llvm_eh_exception.buf', '0', 'void*') }}}) {
+ ___cxa_free_exception({{{ makeGetValue('_llvm_eh_exception.buf', '0', 'void*') }}});
+ {{{ makeSetValue('_llvm_eh_exception.buf', '0', '0', 'void*') }}}
+ }
+ // Clear type.
+ {{{ makeSetValue('_llvm_eh_exception.buf', '4', '0', 'void*') }}}
+ // Call destructor if one is registered then clear it.
+ if ({{{ makeGetValue('_llvm_eh_exception.buf', '8', 'void*') }}}) {
+ FUNCTION_TABLE[{{{ makeGetValue('_llvm_eh_exception.buf', '8', 'void*') }}}]();
+ {{{ makeSetValue('_llvm_eh_exception.buf', '8', '0', 'i32') }}}
+ }
+ },
+ __cxa_get_exception_ptr__deps: ['llvm_eh_exception'],
+ __cxa_get_exception_ptr: function(ptr) {
+ return ptr;
},
__cxa_call_unexpected: function(exception) {
@@ -4018,9 +4066,29 @@ LibraryManager.library = {
throw exception;
},
+ terminate: '__cxa_call_unexpected',
+
__gxx_personality_v0: function() {
},
+ // 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.
+ // type_info for int.
+ _ZTIi: [0],
+ // type_info for long.
+ _ZTIl: [0],
+ // type_info for long long.
+ _ZTIx: [0],
+ // type_info for float.
+ _ZTIf: [0],
+ // type_info for double.
+ _ZTId: [0],
+ // type_info for char.
+ _ZTIc: [0],
+ // type_info for void.
+ _ZTIv: [0],
+
llvm_umul_with_overflow_i32: function(x, y) {
return {
f0: x*y,
diff --git a/tests/exceptions/output.txt b/tests/exceptions/output.txt
new file mode 100644
index 00000000..9f6961e3
--- /dev/null
+++ b/tests/exceptions/output.txt
@@ -0,0 +1,54 @@
+*CREATING A FOO
+*CREATING A BAR
+*CREATING A QUUX
+*CREATING A QUUX
+*CREATING A CHILD
+start
+
+
+ throwing ExFooInstance
+*COPYING A FOO
+*COPYING A FOO
+outer catch foo: 11
+*DESTROYING A FOO
+*DESTROYING A FOO
+
+
+ throwing ExBarInstance
+*COPYING A BAR
+*COPYING A BAR
+inner re-throw: 22
+*DESTROYING A BAR
+outer catch bar-ref: 22
+*DESTROYING A BAR
+
+
+ throwing ExQuuxInstance
+*COPYING A QUUX
+*COPYING A QUUX
+inner catch quux: 33
+*DESTROYING A QUUX
+*DESTROYING A QUUX
+
+
+
+
+
+
+ throwing 42
+outer catch int: 42
+
+
+ throwing NULL
+outer catch-all
+
+
+ not throwing
+
+
+end
+*DESTROYING A CHILD
+*DESTROYING A QUUX
+*DESTROYING A QUUX
+*DESTROYING A BAR
+*DESTROYING A FOO
diff --git a/tests/exceptions/typed.cpp b/tests/exceptions/typed.cpp
new file mode 100644
index 00000000..dd509bdf
--- /dev/null
+++ b/tests/exceptions/typed.cpp
@@ -0,0 +1,91 @@
+#include <stdio.h>
+
+class ExFoo {
+public:
+ int x;
+ ExFoo(int x) { this->x = x; printf("*CREATING A FOO\n"); }
+ ExFoo(const ExFoo& other) { x=other.x; printf("*COPYING A FOO\n"); }
+ ~ExFoo() { printf("*DESTROYING A FOO\n"); }
+} ExFooInstance(11);
+class ExBar {
+public:
+ int x;
+ ExBar(int x) { this->x = x; printf("*CREATING A BAR\n"); }
+ ExBar(const ExBar& other) { x=other.x; printf("*COPYING A BAR\n"); }
+ ~ExBar() { printf("*DESTROYING A BAR\n"); }
+} ExBarInstance(22);
+class ExQuux {
+public:
+ int x;
+ ExQuux(int x) { this->x = x; printf("*CREATING A QUUX\n"); }
+ ExQuux(const ExQuux& other) { x=other.x; printf("*COPYING A QUUX\n"); }
+ ~ExQuux() { printf("*DESTROYING A QUUX\n"); }
+} ExQuuxInstance(33);
+class ExChild : public ExQuux {
+public:
+ int x;
+ ExChild(int x) : ExQuux(x) { this->x = x; printf("*CREATING A CHILD\n"); }
+ ExChild(const ExChild& other) : ExQuux(x) { x=other.x; printf("*COPYING CHILD\n"); }
+ ~ExChild() { printf("*DESTROYING A CHILD\n"); }
+} ExChildInstance(44);
+
+void magic(int which) {
+ try {
+ switch (which) {
+ case 0:
+ printf(" throwing ExFooInstance\n");
+ throw ExFooInstance;
+ case 1:
+ printf(" throwing ExBarInstance\n");
+ throw ExBarInstance;
+ case 2:
+ printf(" throwing ExQuuxInstance\n");
+ throw ExQuuxInstance;
+// NOTE: Throwing pointers and polymorphic matching not supported.
+// case 3:
+// printf(" throwing ExQuux ptr\n");
+// throw &ExQuuxInstance;
+// case 4:
+// printf(" throwing ExChildInstance\n");
+// throw ExChildInstance;
+ case 5:
+ printf(" throwing 42\n");
+ throw 42;
+ case 6:
+ printf(" throwing NULL\n");
+ throw (void*)0;
+ case 7:
+ printf(" not throwing\n");
+ }
+ } catch (ExQuux e1) {
+ printf("inner catch quux: %d\n", e1.x);
+ } catch (ExBar e2) {
+ printf("inner re-throw: %d\n", e2.x);
+ throw;
+ }
+}
+
+int main() {
+ printf("start\n\n\n");
+ for (int i = 0; i < 8; i++) {
+ try {
+ magic(i);
+ } catch (ExFoo e1) {
+ printf("outer catch foo: %d\n", e1.x);
+ } catch (ExBar& e2) {
+ printf("outer catch bar-ref: %d\n", e2.x);
+// NOTE: Throwing pointers and polymorphic matching not supported.
+// } catch (ExQuux& e3) {
+// printf("outer catch quux-ref: %d\n", e3.x);
+// } catch (ExQuux* e4) {
+// printf("outer catch quux-ptr: %d\n", e4->x);
+ } catch (int e5) {
+ printf("outer catch int: %d\n", e5);
+ } catch (...) {
+ printf("outer catch-all\n");
+ }
+ printf("\n\n");
+ }
+ printf("end\n");
+ return 0;
+}
diff --git a/tests/runner.py b/tests/runner.py
index 09ea6745..c52027a5 100644
--- a/tests/runner.py
+++ b/tests/runner.py
@@ -178,7 +178,7 @@ class RunnerCore(unittest.TestCase):
def do_emscripten(self, filename, output_processor=None, append_ext=True, extra_args=[]):
# Run Emscripten
exported_settings = {}
- for setting in ['QUANTUM_SIZE', 'RELOOP', 'OPTIMIZE', 'ASSERTIONS', 'USE_TYPED_ARRAYS', 'SAFE_HEAP', 'CHECK_OVERFLOWS', 'CORRECT_OVERFLOWS', 'CORRECT_SIGNS', 'CHECK_SIGNS', 'CORRECT_OVERFLOWS_LINES', 'CORRECT_SIGNS_LINES', 'CORRECT_ROUNDINGS', 'CORRECT_ROUNDINGS_LINES', 'INVOKE_RUN', 'SAFE_HEAP_LINES', 'INIT_STACK', 'AUTO_OPTIMIZE', 'EXPORTED_FUNCTIONS', 'EXPORTED_GLOBALS', 'BUILD_AS_SHARED_LIB', 'INCLUDE_FULL_LIBRARY', 'RUNTIME_TYPE_INFO', 'DISABLE_EXCEPTIONS']:
+ for setting in ['QUANTUM_SIZE', 'RELOOP', 'OPTIMIZE', 'ASSERTIONS', 'USE_TYPED_ARRAYS', 'SAFE_HEAP', 'CHECK_OVERFLOWS', 'CORRECT_OVERFLOWS', 'CORRECT_SIGNS', 'CHECK_SIGNS', 'CORRECT_OVERFLOWS_LINES', 'CORRECT_SIGNS_LINES', 'CORRECT_ROUNDINGS', 'CORRECT_ROUNDINGS_LINES', 'INVOKE_RUN', 'SAFE_HEAP_LINES', 'INIT_STACK', 'AUTO_OPTIMIZE', 'EXPORTED_FUNCTIONS', 'EXPORTED_GLOBALS', 'BUILD_AS_SHARED_LIB', 'INCLUDE_FULL_LIBRARY', 'RUNTIME_TYPE_INFO', 'DISABLE_EXCEPTIONS', 'EXCEPTION_DEBUG']:
try:
value = eval(setting)
exported_settings[setting] = value
@@ -966,6 +966,13 @@ if 'benchmark' not in sys.argv:
DISABLE_EXCEPTIONS = 1
self.do_test(src, 'Compiled code throwing an exception')
+ def test_typed_exceptions(self):
+ global SAFE_HEAP; SAFE_HEAP = 0 # Throwing null will cause an ignorable null pointer access.
+ global EXCEPTION_DEBUG; EXCEPTION_DEBUG = 0 # Messes up expected output.
+ src = open(path_from_root('tests', 'exceptions', 'typed.cpp'), 'r').read()
+ expected = open(path_from_root('tests', 'exceptions', 'output.txt'), 'r').read()
+ self.do_test(src, expected)
+
def test_class(self):
src = '''
#include <stdio.h>