Skip to content

Commit

Permalink
support exceptions with virtual inheritance, by letting libcxxabi adj…
Browse files Browse the repository at this point in the history
…ust the pointer as an out param; fixes #2531; 1.21.9
  • Loading branch information
kripken committed Jul 28, 2014
1 parent 9f5e322 commit 9aa8ab3
Show file tree
Hide file tree
Showing 6 changed files with 57 additions and 15 deletions.
2 changes: 1 addition & 1 deletion emscripten-version.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
1.21.8
1.21.9

21 changes: 10 additions & 11 deletions src/library.js
Original file line number Diff line number Diff line change
Expand Up @@ -4010,28 +4010,27 @@ LibraryManager.library = {
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.
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)
thrown = {{{ makeGetValue('thrown', '0', '*') }}};
}
assert(throwntype);

var pointer = Module['___cxa_is_pointer_type'](throwntype);
// can_catch receives a **, add indirection
if (!___cxa_find_matching_catch.buffer) ___cxa_find_matching_catch.buffer = _malloc(4);
{{{ makeSetValue('___cxa_find_matching_catch.buffer', '0', 'thrown', '*') }}};
thrown = ___cxa_find_matching_catch.buffer;
// The different catch blocks are denoted by different types.
// Due to inheritance, those types may not precisely match the
// 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 (typeArray[i] && Module['___cxa_can_catch'](typeArray[i], throwntype, thrown)) { // XXX thrown should be an out ptr
if (typeArray[i] && Module['___cxa_can_catch'](typeArray[i], throwntype, thrown)) {
thrown = {{{ makeGetValue('thrown', '0', '*') }}}; // undo indirection
{{{ 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
// typeinfo defined. Best-efforts match just in case.
thrown = {{{ makeGetValue('thrown', '0', '*') }}}; // undo indirection
{{{ makeStructuralReturn(['thrown', 'throwntype']) }}};
},

Expand Down
8 changes: 5 additions & 3 deletions system/lib/libcxxabi/src/private_typeinfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1162,13 +1162,15 @@ __base_class_type_info::search_below_dst(__dynamic_cast_info* info,
#ifdef __EMSCRIPTEN__
extern "C" {

int __cxa_can_catch(__shim_type_info* catchType, __shim_type_info* excpType, void *thrown) {
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);
void *temp = *thrown;
int ret = catchType->can_catch(excpType, temp);
if (ret) *thrown = temp; // apply changes only if we are catching
return ret;
}

int __cxa_is_pointer_type(__shim_type_info* type) {
Expand Down
28 changes: 28 additions & 0 deletions tests/core/test_exceptions_virtual_inheritance.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#include <iostream>
#include <stdexcept>

using std::cout;
using std::endl;

struct my_exception : public virtual std::runtime_error {
// To allow this to be thrown directly in the tests below.
explicit my_exception(const std::string &what)
: std::runtime_error(what)
{}

protected:
my_exception()
// This won't be called because of virtual inheritance.
: std::runtime_error("::my_exception")
{}
};

int main(const int argc, const char * const * const argv) {
try {
cout << "Throwing ::my_exception" << endl;
throw ::my_exception("my_what");
} catch(const std::runtime_error &ex) {
cout << "Caught std::runtime_error: " << ex.what() << endl;
}
}

2 changes: 2 additions & 0 deletions tests/core/test_exceptions_virtual_inheritance.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Throwing ::my_exception
Caught std::runtime_error: my_what
11 changes: 11 additions & 0 deletions tests/test_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -1463,6 +1463,17 @@ def test_exceptions_typed(self):

self.do_run_from_file(src, output)

def test_exceptions_virtual_inheritance(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

test_path = path_from_root('tests', 'core', 'test_exceptions_virtual_inheritance')
src, output = (test_path + s for s in ('.cpp', '.txt'))

self.do_run_from_file(src, output)

def test_exceptions_multi(self):
Settings.DISABLE_EXCEPTION_CATCHING = 0
test_path = path_from_root('tests', 'core', 'test_exceptions_multi')
Expand Down

0 comments on commit 9aa8ab3

Please sign in to comment.