Skip to content

Commit 7df4038

Browse files
authored
Add helper function for C++ exception formatting (#16343)
1 parent 6f3b7ee commit 7df4038

File tree

5 files changed

+118
-1
lines changed

5 files changed

+118
-1
lines changed

emcc.py

+5
Original file line numberDiff line numberDiff line change
@@ -2077,6 +2077,11 @@ def phase_linker_setup(options, state, newargs, user_settings):
20772077
else:
20782078
settings.JS_LIBRARIES.append((0, 'library_pthread_stub.js'))
20792079

2080+
# TODO: Move this into the library JS file once it becomes possible.
2081+
# See https://github.com/emscripten-core/emscripten/pull/15982
2082+
if settings.INCLUDE_FULL_LIBRARY and not settings.DISABLE_EXCEPTION_CATCHING:
2083+
settings.EXPORTED_FUNCTIONS += ['_emscripten_format_exception', '_free']
2084+
20802085
if settings.FORCE_FILESYSTEM and not settings.MINIMAL_RUNTIME:
20812086
# when the filesystem is forced, we export by default methods that filesystem usage
20822087
# may need, including filesystem usage from standalone file packager output (i.e.

src/library_exceptions.js

+10
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,16 @@ var LibraryExceptions = {
428428
catchInfo.free();
429429
{{{ makeThrow('ptr') }}}
430430
},
431+
432+
#if !DISABLE_EXCEPTION_CATCHING
433+
$formatException__deps: ['emscripten_format_exception', 'free'],
434+
$formatException: function(excPtr) {
435+
var utf8_addr = _emscripten_format_exception(excPtr);
436+
var result = UTF8ToString(utf8_addr);
437+
_free(utf8_addr);
438+
return result;
439+
},
440+
#endif
431441
};
432442

433443
// In LLVM, exceptions generate a set of functions of form __cxa_find_matching_catch_1(), __cxa_find_matching_catch_2(), etc.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
#include "cxa_exception.h"
2+
#include "cxxabi.h"
3+
#include <stdio.h>
4+
#include <typeinfo>
5+
6+
#ifdef __USING_EMSCRIPTEN_EXCEPTIONS__
7+
8+
extern "C" {
9+
10+
int __cxa_can_catch(const std::type_info* catchType,
11+
const std::type_info* excpType,
12+
void** thrown);
13+
14+
char* emscripten_format_exception(void* exc_ptr) {
15+
__cxxabiv1::__cxa_exception* exc_info =
16+
(__cxxabiv1::__cxa_exception*)exc_ptr - 1;
17+
std::type_info* exc_type = exc_info->exceptionType;
18+
const char* exc_name = exc_type->name();
19+
20+
int status = 0;
21+
char* demangled_buf = __cxxabiv1::__cxa_demangle(exc_name, 0, 0, &status);
22+
if (status == 0 && demangled_buf) {
23+
exc_name = demangled_buf;
24+
}
25+
26+
int can_catch = __cxa_can_catch(&typeid(std::exception), exc_type, &exc_ptr);
27+
char* result = NULL;
28+
if (can_catch) {
29+
const char* exc_what = ((std::exception*)exc_ptr)->what();
30+
asprintf(&result, "Cpp Exception %s: %s", exc_name, exc_what);
31+
} else {
32+
asprintf(&result,
33+
"Cpp Exception: The exception is an object of type '%s' at "
34+
"address %p which does not inherit from std::exception",
35+
exc_name,
36+
exc_ptr);
37+
}
38+
39+
if (demangled_buf) {
40+
free(demangled_buf);
41+
}
42+
return result;
43+
}
44+
}
45+
46+
#endif // __USING_EMSCRIPTEN_EXCEPTIONS__

tests/test_core.py

+55
Original file line numberDiff line numberDiff line change
@@ -1543,6 +1543,61 @@ def test_exceptions_rethrow_missing(self):
15431543
create_file('main.cpp', 'int main() { throw; }')
15441544
self.do_runf('main.cpp', None, assert_returncode=NON_ZERO)
15451545

1546+
def test_format_exception(self):
1547+
self.set_setting('DISABLE_EXCEPTION_CATCHING', 0)
1548+
self.set_setting('DEFAULT_LIBRARY_FUNCS_TO_INCLUDE', ['$formatException'])
1549+
self.set_setting('EXPORTED_FUNCTIONS', ['_main', 'formatException', '_emscripten_format_exception', '_free'])
1550+
self.maybe_closure()
1551+
src = '''
1552+
#include <emscripten.h>
1553+
#include <exception>
1554+
#include <stdexcept>
1555+
using namespace std;
1556+
1557+
class myexception : public exception {
1558+
virtual const char* what() const throw() { return "My exception happened"; }
1559+
} myex;
1560+
1561+
EMSCRIPTEN_KEEPALIVE extern "C" void throw_exc(int x) {
1562+
if (x == 1) {
1563+
throw 1000;
1564+
}
1565+
if (x == 2) {
1566+
throw 'c';
1567+
}
1568+
if (x == 3) {
1569+
throw runtime_error("abc");
1570+
}
1571+
if (x == 4) {
1572+
throw myex;
1573+
}
1574+
if (x == 5) {
1575+
throw "abc";
1576+
}
1577+
}
1578+
1579+
int main() {
1580+
EM_ASM({
1581+
for (let i = 1; i < 6; i++){
1582+
try {
1583+
Module["_throw_exc"](i);
1584+
} catch(p) {
1585+
console.log(Module["formatException"](p).replace(/0x[0-9a-f]*/, "xxx"));
1586+
}
1587+
}
1588+
});
1589+
}
1590+
'''
1591+
expected = '''\
1592+
Cpp Exception: The exception is an object of type 'int' at address xxx which does not inherit from std::exception
1593+
Cpp Exception: The exception is an object of type 'char' at address xxx which does not inherit from std::exception
1594+
Cpp Exception std::runtime_error: abc
1595+
Cpp Exception myexception: My exception happened
1596+
Cpp Exception: The exception is an object of type 'char const*' at address xxx which does not inherit from std::exception
1597+
'''
1598+
1599+
self.do_run(src, expected)
1600+
15461601
@with_both_eh_sjlj
15471602
def test_bad_typeid(self):
15481603
self.do_run(r'''

tools/system_libs.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -1127,7 +1127,8 @@ def get_files(self):
11271127
'stdlib_exception.cpp',
11281128
'stdlib_stdexcept.cpp',
11291129
'stdlib_typeinfo.cpp',
1130-
'private_typeinfo.cpp'
1130+
'private_typeinfo.cpp',
1131+
'format_exception.cpp',
11311132
]
11321133
if self.eh_mode == Exceptions.NONE:
11331134
filenames += ['cxa_noexception.cpp']

0 commit comments

Comments
 (0)