Skip to content

Commit 2d33bff

Browse files
committed
[EmscriptenEH] Always use custom JS class for C/C++ exceptions
This has a slight codesize cost but it means we no longer have to worry about the ambiguity in when we catch a Number.
1 parent 0182d72 commit 2d33bff

19 files changed

Lines changed: 119 additions & 121 deletions

src/lib/libcore.js

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -484,11 +484,7 @@ addToLibrary({
484484
// a proxy and declare the dependency here.
485485
_emscripten_throw_longjmp__deps: ['setThrew'],
486486
_emscripten_throw_longjmp: () => {
487-
#if EXCEPTION_STACK_TRACES
488487
throw new EmscriptenSjLj;
489-
#else
490-
throw Infinity;
491-
#endif
492488
},
493489
#elif !SUPPORT_LONGJMP
494490
#if !INCLUDE_FULL_LIBRARY

src/lib/libdylink.js

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -93,16 +93,9 @@ var LibraryDylink = {
9393
} catch(e) {
9494
stackRestore(sp);
9595
// Create a try-catch guard that rethrows the Emscripten EH exception.
96-
#if EXCEPTION_STACK_TRACES
9796
// Exceptions thrown from C++ and longjmps will be an instance of
9897
// EmscriptenEH.
9998
if (!(e instanceof EmscriptenEH)) throw e;
100-
#else
101-
// Exceptions thrown from C++ will be a pointer (number) and longjmp
102-
// will throw the number Infinity. Use the compact and fast "e !== e+0"
103-
// test to check if e was not a Number.
104-
if (e !== e+0) throw e;
105-
#endif
10699
_setThrew(1, 0);
107100
#if WASM_BIGINT
108101
// In theory this if statement could be done on

src/lib/libemval.js

Lines changed: 3 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -393,35 +393,18 @@ ${functionBody}
393393
},
394394

395395
_emval_throw__deps: ['$Emval',
396-
#if !WASM_EXCEPTIONS
396+
#if !WASM_EXCEPTIONS && !DISABLE_EXCEPTION_CATCHING
397397
'$exceptionLast',
398398
#endif
399399
],
400400
_emval_throw: (object) => {
401401
object = Emval.toValue(object);
402-
#if !WASM_EXCEPTIONS
402+
#if !WASM_EXCEPTIONS && !DISABLE_EXCEPTION_CATCHING
403403
// If we are throwing Emcripten C++ exception, set exceptionLast, as we do
404-
// in __cxa_throw. When EXCEPTION_STACK_TRACES is set, a C++ exception will
405-
// be an instance of EmscriptenEH, and when EXCEPTION_STACK_TRACES is not
406-
// set, it will be a pointer (number).
407-
//
408-
// This is different from __cxa_throw() in libexception.js because
409-
// __cxa_throw() is called from the user C++ code when the 'throw' keyword
410-
// is used, and the value thrown is a C++ pointer. When
411-
// EXCEPTION_STACK_TRACES is true, we wrap it with CppException. But this
412-
// _emval_throw is called when we throw whatever is contained in 'object',
413-
// which can be anything including a CppException object, or a number, or
414-
// other JS object. So we don't use storeException() wrapper here and we
415-
// throw it as is.
416-
#if EXCEPTION_STACK_TRACES
404+
// in __cxa_throw. C++ exception will be an instance of CppEmscripten.
417405
if (object instanceof CppException) {
418406
exceptionLast = object;
419407
}
420-
#else
421-
if (object === object+0) { // Check if it is a number
422-
exceptionLast = object;
423-
}
424-
#endif
425408
#endif
426409
throw object;
427410
},

src/lib/libexceptions.js

Lines changed: 38 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77
var LibraryExceptions = {
88
#if !WASM_EXCEPTIONS
99
$uncaughtExceptionCount: '0',
10-
$exceptionLast: '0',
10+
#if !DISABLE_EXCEPTION_CATCHING
11+
$exceptionLast: null,
12+
#endif
1113
$exceptionCaught: ' []',
1214

1315
// This class is the exception metadata which is prepended to each thrown object (in WASM memory).
@@ -82,14 +84,15 @@ var LibraryExceptions = {
8284

8385
// Here, we throw an exception after recording a couple of values that we need to remember
8486
// We also remember that it was the last exception thrown as we need to know that later.
85-
__cxa_throw__deps: ['$ExceptionInfo', '$exceptionLast', '$uncaughtExceptionCount',
87+
__cxa_throw__deps: ['$ExceptionInfo', '$uncaughtExceptionCount',
8688
#if !DISABLE_EXCEPTION_CATCHING
89+
'$exceptionLast',
8790
'__cxa_increment_exception_refcount',
8891
#endif
8992
#if EXCEPTION_STACK_TRACES
90-
// When EXCEPTION_STACK_TRACES is enabled, storeException contains a call to
91-
// 'new CppException', whose constructor calls getExceptionMessage. We can't
92-
// track the dependency there, so we track it here.
93+
// When EXCEPTION_STACK_TRACES is enabled, the 'CppException' constructor
94+
// calls getExceptionMessage. We can't track the dependency there, so we
95+
// track it here.
9396
'$getExceptionMessage',
9497
// These functions can be necessary to prevent memory leaks from the JS
9598
// side. Even though they are not used it here directly, we export them when
@@ -106,17 +109,18 @@ var LibraryExceptions = {
106109
info.init(type, destructor);
107110
#if !DISABLE_EXCEPTION_CATCHING
108111
___cxa_increment_exception_refcount(ptr);
112+
exceptionLast = new CppException(ptr);
109113
#endif
110-
{{{ storeException('exceptionLast', 'ptr') }}}
111114
uncaughtExceptionCount++;
112-
{{{ makeThrow('exceptionLast') }}}
115+
{{{ makeThrow() }}}
113116
},
114117

115118
// This exception will be caught twice, but while begin_catch runs twice,
116119
// we early-exit from end_catch when the exception has been rethrown, so
117120
// pop that here from the caught exceptions.
118-
__cxa_rethrow__deps: ['$exceptionCaught', '$exceptionLast', '$uncaughtExceptionCount',
121+
__cxa_rethrow__deps: ['$exceptionCaught', '$uncaughtExceptionCount',
119122
#if !DISABLE_EXCEPTION_CATCHING
123+
'$exceptionLast',
120124
'__cxa_increment_exception_refcount',
121125
#endif
122126
],
@@ -140,8 +144,10 @@ var LibraryExceptions = {
140144
dbg('__cxa_rethrow, popped ' +
141145
[ptrToString(ptr), exceptionLast, 'stack', exceptionCaught]);
142146
#endif
143-
{{{ storeException('exceptionLast', 'ptr') }}}
144-
{{{ makeThrow('exceptionLast') }}}
147+
#if !DISABLE_EXCEPTION_CATCHING
148+
exceptionLast = new CppException(ptr);
149+
#endif
150+
{{{ makeThrow() }}}
145151
},
146152

147153
llvm_eh_typeid_for: (type) => type,
@@ -166,7 +172,11 @@ var LibraryExceptions = {
166172
// and free the exception. Note that if the dynCall on the destructor fails
167173
// due to calling apply on undefined, that means that the destructor is
168174
// an invalid index into the FUNCTION_TABLE, so something has gone wrong.
169-
__cxa_end_catch__deps: ['$exceptionCaught', '$exceptionLast', '__cxa_decrement_exception_refcount', 'setThrew'],
175+
__cxa_end_catch__deps: ['$exceptionCaught', '__cxa_decrement_exception_refcount', 'setThrew',
176+
#if !DISABLE_EXCEPTION_CATCHING
177+
'$exceptionLast',
178+
#endif
179+
],
170180
__cxa_end_catch: () => {
171181
// Clear state flag.
172182
_setThrew(0, 0);
@@ -177,10 +187,12 @@ var LibraryExceptions = {
177187
var info = exceptionCaught.pop();
178188

179189
#if EXCEPTION_DEBUG
180-
dbg('__cxa_end_catch popped ' + [info, exceptionLast, 'stack', exceptionCaught]);
190+
dbg('__cxa_end_catch popped ' + [info, 'stack', exceptionCaught]);
181191
#endif
182192
___cxa_decrement_exception_refcount(info.excPtr);
183-
exceptionLast = 0; // XXX in decRef?
193+
#if !DISABLE_EXCEPTION_CATCHING
194+
exceptionLast = null; // XXX in decRef?
195+
#endif
184196
},
185197

186198
__cxa_uncaught_exceptions__deps: ['$uncaughtExceptionCount'],
@@ -224,14 +236,15 @@ var LibraryExceptions = {
224236
// unwinding using 'if' blocks around each function, so the remaining
225237
// functionality boils down to picking a suitable 'catch' block.
226238
// We'll do that here, instead, to keep things simpler.
239+
#if !DISABLE_EXCEPTION_CATCHING
227240
$findMatchingCatch__deps: ['$exceptionLast', '$ExceptionInfo', '__cxa_can_catch', '$setTempRet0'],
241+
#endif
228242
$findMatchingCatch: (args) => {
229-
var thrown =
230-
#if EXCEPTION_STACK_TRACES
231-
exceptionLast?.excPtr;
243+
#if DISABLE_EXCEPTION_CATCHING
244+
setTempRet0(0);
245+
return 0;
232246
#else
233-
exceptionLast;
234-
#endif
247+
var thrown = exceptionLast?.excPtr;
235248
if (!thrown) {
236249
// just pass through the null ptr
237250
setTempRet0(0);
@@ -270,17 +283,22 @@ var LibraryExceptions = {
270283
}
271284
setTempRet0(thrownType);
272285
return thrown;
286+
#endif
273287
},
274288

289+
#if !DISABLE_EXCEPTION_CATCHING
275290
__resumeException__deps: ['$exceptionLast'],
291+
#endif
276292
__resumeException: (ptr) => {
293+
#if !DISABLE_EXCEPTION_CATCHING
277294
#if EXCEPTION_DEBUG
278295
dbg("__resumeException " + [ptrToString(ptr), exceptionLast]);
279296
#endif
280297
if (!exceptionLast) {
281-
{{{ storeException('exceptionLast', 'ptr') }}}
298+
exceptionLast = new CppException(ptr);
282299
}
283-
{{{ makeThrow('exceptionLast') }}}
300+
#endif
301+
{{{ makeThrow() }}}
284302
},
285303

286304
#endif

src/parseTools.mjs

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -648,22 +648,21 @@ export function makeReturn64(value) {
648648
return `(setTempRet0(${pair[1]}), ${pair[0]})`;
649649
}
650650

651-
function makeThrow(excPtr) {
652-
if (ASSERTIONS && DISABLE_EXCEPTION_CATCHING) {
653-
var assertInfo =
654-
'Exception thrown, but exception catching is not enabled. Compile with -sNO_DISABLE_EXCEPTION_CATCHING or -sEXCEPTION_CATCHING_ALLOWED=[..] to catch.';
655-
if (MAIN_MODULE) {
656-
assertInfo +=
657-
' (note: in dynamic linking, if a side module wants exceptions, the main module must be built with that support)';
651+
function makeThrow() {
652+
if (DISABLE_EXCEPTION_CATCHING) {
653+
if (ASSERTIONS) {
654+
var assertInfo =
655+
'Exception thrown, but exception catching is not enabled. Compile with -sNO_DISABLE_EXCEPTION_CATCHING or -sEXCEPTION_CATCHING_ALLOWED=[..] to catch.';
656+
if (MAIN_MODULE) {
657+
assertInfo +=
658+
' (note: in dynamic linking, if a side module wants exceptions, the main module must be built with that support)';
659+
}
660+
return `assert(false, '${assertInfo}');`;
661+
} else {
662+
return 'abort()';
658663
}
659-
return `assert(false, '${assertInfo}');`;
660664
}
661-
return `throw ${excPtr};`;
662-
}
663-
664-
function storeException(varName, excPtr) {
665-
var exceptionToStore = EXCEPTION_STACK_TRACES ? `new CppException(${excPtr})` : `${excPtr}`;
666-
return `${varName} = ${exceptionToStore};`;
665+
return 'throw exceptionLast;';
667666
}
668667

669668
function charCode(char) {
@@ -1255,7 +1254,6 @@ addToCompileTimeContext({
12551254
runtimeKeepalivePop,
12561255
runtimeKeepalivePush,
12571256
splitI64,
1258-
storeException,
12591257
to64,
12601258
toIndexType,
12611259
nodePthreadDetection,

src/preamble.js

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -360,11 +360,7 @@ function makeAbortWrapper(original) {
360360
ABORT // rethrow exception if abort() was called in the original function call above
361361
|| abortWrapperDepth > 1 // rethrow exceptions not caught at the top level if exception catching is enabled; rethrow from exceptions from within callMain
362362
#if SUPPORT_LONGJMP == 'emscripten' // Rethrow longjmp if enabled
363-
#if EXCEPTION_STACK_TRACES
364-
|| e instanceof EmscriptenSjLj // EXCEPTION_STACK_TRACES=1 will throw an instance of EmscriptenSjLj
365-
#else
366-
|| e === Infinity // EXCEPTION_STACK_TRACES=0 will throw Infinity
367-
#endif // EXCEPTION_STACK_TRACES
363+
|| e instanceof EmscriptenSjLj
368364
#endif
369365
|| e === 'unwind'
370366
) {

src/runtime_exceptions.js

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,35 @@
44
* SPDX-License-Identifier: MIT
55
*/
66

7-
#if EXCEPTION_STACK_TRACES && !WASM_EXCEPTIONS
7+
#if !WASM_EXCEPTIONS
8+
89
// Base Emscripten EH error class
10+
#if EXCEPTION_STACK_TRACES
911
class EmscriptenEH extends Error {}
12+
#else
13+
class EmscriptenEH {}
14+
#endif
1015

1116
#if SUPPORT_LONGJMP == 'emscripten'
1217
class EmscriptenSjLj extends EmscriptenEH {}
1318
#endif
1419

20+
#if !DISABLE_EXCEPTION_CATCHING
1521
class CppException extends EmscriptenEH {
1622
constructor(excPtr) {
23+
#if EXCEPTION_STACK_TRACES
1724
super(excPtr);
25+
#else
26+
super();
27+
#endif
1828
this.excPtr = excPtr;
19-
#if !DISABLE_EXCEPTION_CATCHING
29+
#if !DISABLE_EXCEPTION_CATCHING && EXCEPTION_STACK_TRACES
2030
const excInfo = getExceptionMessage(excPtr);
2131
this.name = excInfo[0];
2232
this.message = excInfo[1];
2333
#endif
2434
}
2535
}
26-
#endif // EXCEPTION_STACK_TRACES && !WASM_EXCEPTIONS
36+
#endif
37+
38+
#endif // !WASM_EXCEPTIONS

test/codesize/test_codesize_cxx_ctors1.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
{
2-
"a.out.js": 19214,
3-
"a.out.js.gz": 7981,
2+
"a.out.js": 19193,
3+
"a.out.js.gz": 7970,
44
"a.out.nodebug.wasm": 132638,
55
"a.out.nodebug.wasm.gz": 49927,
6-
"total": 151852,
7-
"total_gz": 57908,
6+
"total": 151831,
7+
"total_gz": 57897,
88
"sent": [
99
"__cxa_throw",
1010
"_abort_js",

test/codesize/test_codesize_cxx_ctors2.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
{
2-
"a.out.js": 19191,
3-
"a.out.js.gz": 7966,
2+
"a.out.js": 19170,
3+
"a.out.js.gz": 7957,
44
"a.out.nodebug.wasm": 132064,
55
"a.out.nodebug.wasm.gz": 49586,
6-
"total": 151255,
7-
"total_gz": 57552,
6+
"total": 151234,
7+
"total_gz": 57543,
88
"sent": [
99
"__cxa_throw",
1010
"_abort_js",

test/codesize/test_codesize_cxx_except.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
{
2-
"a.out.js": 22875,
3-
"a.out.js.gz": 8956,
2+
"a.out.js": 23195,
3+
"a.out.js.gz": 8968,
44
"a.out.nodebug.wasm": 172516,
55
"a.out.nodebug.wasm.gz": 57438,
6-
"total": 195391,
7-
"total_gz": 66394,
6+
"total": 195711,
7+
"total_gz": 66406,
88
"sent": [
99
"__cxa_begin_catch",
1010
"__cxa_end_catch",

0 commit comments

Comments
 (0)