blob: 89e7fd11dac7b8ea46e95a3ec9243d579c0b45cf [file] [log] [blame] [edit]
/*
* Copyright 2010 The Emscripten Authors. All rights reserved.
* Emscripten is available under two separate licenses, the MIT license and the
* University of Illinois/NCSA Open Source License. Both these licenses can be
* found in the LICENSE file.
*
* C++ exception handling support.
*/
var LibraryExceptions = {
__exception_last: '0',
__exception_caught: ' []',
__exception_infos: '{}',
__exception_deAdjust__deps: ['__exception_infos'],
__exception_deAdjust: function(adjusted) {
if (!adjusted || ___exception_infos[adjusted]) return adjusted;
for (var key in ___exception_infos) {
var ptr = +key; // the iteration key is a string, and if we throw this, it must be an integer as that is what we look for
var adj = ___exception_infos[ptr].adjusted;
var len = adj.length;
for (var i = 0; i < len; i++) {
if (adj[i] === adjusted) {
#if EXCEPTION_DEBUG
err('de-adjusted exception ptr ' + adjusted + ' to ' + ptr);
#endif
return ptr;
}
}
}
#if EXCEPTION_DEBUG
err('no de-adjustment for unknown exception ptr ' + adjusted);
#endif
return adjusted;
},
__exception_addRef__deps: ['__exception_infos'],
__exception_addRef: function(ptr) {
#if EXCEPTION_DEBUG
err('addref ' + ptr);
#endif
if (!ptr) return;
var info = ___exception_infos[ptr];
info.refcount++;
},
__exception_decRef__deps: ['__exception_infos', '__cxa_free_exception'
#if EXCEPTION_DEBUG
, '__exception_last', '__exception_caught'
#endif
],
__exception_decRef: function(ptr) {
#if EXCEPTION_DEBUG
err('decref ' + ptr);
#endif
if (!ptr) return;
var info = ___exception_infos[ptr];
#if ASSERTIONS
assert(info.refcount > 0);
#endif
info.refcount--;
// A rethrown exception can reach refcount 0; it must not be discarded
// Its next handler will clear the rethrown flag and addRef it, prior to
// final decRef and destruction here
if (info.refcount === 0 && !info.rethrown) {
if (info.destructor) {
#if WASM_BACKEND == 0
Module['dynCall_vi'](info.destructor, ptr);
#else
// In Wasm, destructors return 'this' as in ARM
Module['dynCall_ii'](info.destructor, ptr);
#endif
}
delete ___exception_infos[ptr];
___cxa_free_exception(ptr);
#if EXCEPTION_DEBUG
err('decref freeing exception ' + [ptr, ___exception_last, 'stack', ___exception_caught]);
#endif
}
},
__exception_clearRef__deps: ['__exception_infos'],
__exception_clearRef: function(ptr) {
if (!ptr) return;
var info = ___exception_infos[ptr];
info.refcount = 0;
},
// Exceptions
__cxa_allocate_exception: function(size) {
return _malloc(size);
},
__cxa_free_exception: function(ptr) {
#if ABORTING_MALLOC || ASSERTIONS
try {
#endif
return _free(ptr);
#if ABORTING_MALLOC || ASSERTIONS
} catch(e) {
#if ASSERTIONS
err('exception during cxa_free_exception: ' + e);
#endif
}
#endif
},
__cxa_increment_exception_refcount__deps: ['__exception_addRef', '__exception_deAdjust'],
__cxa_increment_exception_refcount: function(ptr) {
___exception_addRef(___exception_deAdjust(ptr));
},
__cxa_decrement_exception_refcount__deps: ['__exception_decRef', '__exception_deAdjust'],
__cxa_decrement_exception_refcount: function(ptr) {
___exception_decRef(___exception_deAdjust(ptr));
},
// Here, we throw an exception after recording a couple of values that we need to remember
// We also remember that it was the last exception thrown as we need to know that later.
__cxa_throw__sig: 'viii',
__cxa_throw__deps: ['__exception_infos', '__exception_last', '_ZSt18uncaught_exceptionv'],
__cxa_throw: function(ptr, type, destructor) {
#if EXCEPTION_DEBUG
err('Compiled code throwing an exception, ' + [ptr,type,destructor]);
#endif
___exception_infos[ptr] = {
ptr: ptr,
adjusted: [ptr],
type: type,
destructor: destructor,
refcount: 0,
caught: false,
rethrown: false
};
___exception_last = ptr;
if (!("uncaught_exception" in __ZSt18uncaught_exceptionv)) {
__ZSt18uncaught_exceptionv.uncaught_exceptions = 1;
} else {
__ZSt18uncaught_exceptionv.uncaught_exceptions++;
}
{{{ makeThrow('ptr') }}}
},
// This exception will be caught twice, but while begin_catch runs twice,
// we early-exit from end_catch when the exception has been rethrown, so
// pop that here from the caught exceptions.
__cxa_rethrow__deps: ['__exception_caught', '__exception_deAdjust', '__exception_infos', '__exception_last'],
__cxa_rethrow: function() {
var ptr = ___exception_caught.pop();
ptr = ___exception_deAdjust(ptr);
if (!___exception_infos[ptr].rethrown) {
// Only pop if the corresponding push was through rethrow_primary_exception
___exception_caught.push(ptr);
___exception_infos[ptr].rethrown = true;
}
#if EXCEPTION_DEBUG
err('Compiled code RE-throwing an exception, popped ' + [ptr, ___exception_last, 'stack', ___exception_caught]);
#endif
___exception_last = ptr;
{{{ makeThrow('ptr') }}}
},
llvm_eh_exception__deps: ['__exception_last'],
llvm_eh_exception: function() {
return ___exception_last;
},
llvm_eh_selector__jsargs: true,
llvm_eh_selector__deps: ['__exception_last'],
llvm_eh_selector: function(unused_exception_value, personality/*, varargs*/) {
var type = ___exception_last;
for (var i = 2; i < arguments.length; i++) {
if (arguments[i] == type) return type;
}
return 0;
},
llvm_eh_typeid_for: function(type) {
return type;
},
__cxa_begin_catch__deps: ['__exception_infos', '__exception_caught', '__exception_addRef', '__exception_deAdjust', '_ZSt18uncaught_exceptionv'],
__cxa_begin_catch: function(ptr) {
var info = ___exception_infos[ptr];
if (info && !info.caught) {
info.caught = true;
__ZSt18uncaught_exceptionv.uncaught_exceptions--;
}
if (info) info.rethrown = false;
___exception_caught.push(ptr);
#if EXCEPTION_DEBUG
err('cxa_begin_catch ' + [ptr, 'stack', ___exception_caught]);
#endif
___exception_addRef(___exception_deAdjust(ptr));
return ptr;
},
// We're done with a catch. Now, we can run the destructor if there is one
// and free the exception. Note that if the dynCall on the destructor fails
// due to calling apply on undefined, that means that the destructor is
// an invalid index into the FUNCTION_TABLE, so something has gone wrong.
__cxa_end_catch__deps: ['__exception_caught', '__exception_last', '__exception_decRef', '__exception_deAdjust', 'setThrew'],
__cxa_end_catch: function() {
// Clear state flag.
_setThrew(0);
// Call destructor if one is registered then clear it.
var ptr = ___exception_caught.pop();
#if EXCEPTION_DEBUG
err('cxa_end_catch popped ' + [ptr, ___exception_last, 'stack', ___exception_caught]);
#endif
if (ptr) {
___exception_decRef(___exception_deAdjust(ptr));
___exception_last = 0; // XXX in decRef?
}
},
__cxa_get_exception_ptr: function(ptr) {
#if EXCEPTION_DEBUG
err('cxa_get_exception_ptr ' + ptr);
#endif
// TODO: use info.adjusted?
return ptr;
},
_ZSt18uncaught_exceptionv: function() { // std::uncaught_exception()
return __ZSt18uncaught_exceptionv.uncaught_exceptions > 0;
},
__cxa_uncaught_exceptions__deps: ['_ZSt18uncaught_exceptionv'],
__cxa_uncaught_exceptions: function() {
return __ZSt18uncaught_exceptionv.uncaught_exceptions;
},
__cxa_call_unexpected: function(exception) {
err('Unexpected exception thrown, this is not properly supported - aborting');
#if !MINIMAL_RUNTIME
ABORT = true;
#endif
throw exception;
},
__cxa_current_primary_exception__deps: ['__exception_caught', '__exception_addRef', '__exception_deAdjust'],
__cxa_current_primary_exception: function() {
var ret = ___exception_caught[___exception_caught.length-1] || 0;
if (ret) ___exception_addRef(___exception_deAdjust(ret));
return ret;
},
__cxa_rethrow_primary_exception__deps: ['__exception_deAdjust', '__exception_caught', '__exception_infos', '__cxa_rethrow'],
__cxa_rethrow_primary_exception: function(ptr) {
if (!ptr) return;
ptr = ___exception_deAdjust(ptr);
___exception_caught.push(ptr);
___exception_infos[ptr].rethrown = true;
___cxa_rethrow();
},
// 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
// relating to the context of the exception and makes judgements
// about how to handle it. Some of it is about matching a suitable
// catch clause, and some of it is about unwinding. We already handle
// unwinding using 'if' blocks around each function, so the remaining
// functionality boils down to picking a suitable 'catch' block.
// We'll do that here, instead, to keep things simpler.
__cxa_find_matching_catch__deps: ['__exception_last', '__exception_infos', '__resumeException'],
__cxa_find_matching_catch: function() {
var thrown = ___exception_last;
if (!thrown) {
// just pass through the null ptr
{{{ makeStructuralReturn([0, 0]) }}};
}
var info = ___exception_infos[thrown];
var throwntype = info.type;
if (!throwntype) {
// just pass through the thrown ptr
{{{ makeStructuralReturn(['thrown', 0]) }}};
}
var typeArray = Array.prototype.slice.call(arguments);
var pointer = {{{ exportedAsmFunc('___cxa_is_pointer_type') }}}(throwntype);
// can_catch receives a **, add indirection
#if EXCEPTION_DEBUG
out("can_catch on " + [thrown]);
#endif
#if DISABLE_EXCEPTION_CATCHING == 1
var buffer = 0;
#else
var buffer = {{{ makeStaticAlloc(4) }}};
#endif
{{{ makeSetValue('buffer', '0', 'thrown', '*') }}};
thrown = 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] && {{{ exportedAsmFunc('___cxa_can_catch') }}}(typeArray[i], throwntype, thrown)) {
thrown = {{{ makeGetValue('thrown', '0', '*') }}}; // undo indirection
info.adjusted.push(thrown);
#if EXCEPTION_DEBUG
out(" can_catch found " + [thrown, typeArray[i]]);
#endif
{{{ 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']) }}};
},
__resumeException__deps: [function() { '__exception_last', Functions.libraryFunctions['___resumeException'] = 1 }], // will be called directly from compiled code
__resumeException: function(ptr) {
#if EXCEPTION_DEBUG
out("Resuming exception " + [ptr, ___exception_last]);
#endif
if (!___exception_last) { ___exception_last = ptr; }
{{{ makeThrow('ptr') }}}
},
};
// In LLVM, exceptions generate a set of functions of form __cxa_find_matching_catch_1(), __cxa_find_matching_catch_2(), etc.
// where the number specifies the number of arguments. In Emscripten, route all these to a single function '__cxa_find_matching_catch'
// that variadically processes all of these functions using JS 'arguments' object.
addCxaCatch = function(n) {
LibraryManager.library['__cxa_find_matching_catch_' + n] = LibraryExceptions['__cxa_find_matching_catch'];
LibraryManager.library['__cxa_find_matching_catch_' + n + '__sig'] = new Array(n + 2).join('i');
};
mergeInto(LibraryManager.library, LibraryExceptions);