diff options
author | Adrian Taylor <adrian@macrobug.com> | 2012-02-10 23:11:34 +0000 |
---|---|---|
committer | Adrian Taylor <adrian@macrobug.com> | 2012-02-20 08:50:04 +0000 |
commit | e6f092d69f1806153571e6e808caea76f8bf5190 (patch) | |
tree | 0ed8e824e097d43cd3b6a03e39bb79c627a2deef /src | |
parent | 1d5093e31274f666b052e0ac392a396f3e3b1875 (diff) |
Polymorphic exception handling.
Previously exception handling only worked if there were a 'catch' block which precisely matched
the type of the thrown exception.
That's not always the case if we're trying to catch subclasses.
This change enhances behaviour to match subclasses, and also covers some other cases where
we weren't catching the right thing.
Diffstat (limited to 'src')
-rw-r--r-- | src/intertyper.js | 13 | ||||
-rw-r--r-- | src/jsifier.js | 5 | ||||
-rw-r--r-- | src/library.js | 73 | ||||
-rw-r--r-- | src/parseTools.js | 6 |
4 files changed, 91 insertions, 6 deletions
diff --git a/src/intertyper.js b/src/intertyper.js index 91ad15eb..cfb12331 100644 --- a/src/intertyper.js +++ b/src/intertyper.js @@ -728,11 +728,22 @@ function intertyper(data, sidePass, baseLineNums) { this.forwardItem(item, 'Reintegrator'); } }); - // 'landingpad' - just a stub implementation + // 'landingpad' substrate.addActor('Landingpad', { processItem: function(item) { item.intertype = 'landingpad'; item.type = item.tokens[1].text; + item.catchables = []; + var catchIdx = findTokenText(item, "catch"); + if (catchIdx != -1) { + do { + var nextCatchIdx = findTokenTextAfter(item, "catch", catchIdx+1); + if (nextCatchIdx == -1) + nextCatchIdx = item.tokens.length; + item.catchables.push(parseLLVMSegment(item.tokens.slice(catchIdx+2, nextCatchIdx))); + catchIdx = nextCatchIdx; + } while (catchIdx != item.tokens.length); + } Types.needAnalysis[item.type] = 0; this.forwardItem(item, 'Reintegrator'); } diff --git a/src/jsifier.js b/src/jsifier.js index b830fc7c..ea4dff06 100644 --- a/src/jsifier.js +++ b/src/jsifier.js @@ -972,9 +972,8 @@ function JSify(data, functionsOnly, givenFunctions) { } }); makeFuncLineActor('landingpad', function(item) { - // Just a stub - return '{ f0: ' + makeGetValue('_llvm_eh_exception.buf', '0', 'void*') + - ', f1:' + makeGetValue('_llvm_eh_exception.buf', QUANTUM_SIZE, 'void*') + ' }'; + var catchTypeArray = item.catchables.map(finalizeLLVMParameter).join(','); + return '___cxa_find_matching_catch('+ makeGetValue('_llvm_eh_exception.buf', '0', 'void*') +',' + makeGetValue('_llvm_eh_exception.buf', QUANTUM_SIZE, 'void*') + ',[' + catchTypeArray +'])'; }); makeFuncLineActor('load', function(item) { var value = finalizeLLVMParameter(item.pointer); diff --git a/src/library.js b/src/library.js index 39f7867d..6feb37e4 100644 --- a/src/library.js +++ b/src/library.js @@ -4374,6 +4374,7 @@ LibraryManager.library = { __cxa_guard_release: function() {}, __cxa_guard_abort: function() {}, + _ZTVN10__cxxabiv119__pointer_type_infoE: [0], // is a pointer _ZTVN10__cxxabiv117__class_type_infoE: [1], // no inherited classes _ZTVN10__cxxabiv120__si_class_type_infoE: [2], // yes inherited classes @@ -4402,7 +4403,7 @@ LibraryManager.library = { __cxa_free_exception: function(ptr) { return _free(ptr); }, - __cxa_throw__deps: ['llvm_eh_exception', '_ZSt18uncaught_exceptionv'], + __cxa_throw__deps: ['llvm_eh_exception', '_ZSt18uncaught_exceptionv', '__cxa_find_matching_catch'], __cxa_throw: function(ptr, type, destructor) { #if EXCEPTION_DEBUG print('Compiled code throwing an exception, ' + [ptr,type,destructor] + ', at ' + new Error().stack); @@ -4486,6 +4487,74 @@ LibraryManager.library = { __gxx_personality_v0: function() { }, + // 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: ['__cxa_does_inherit'], + __cxa_find_matching_catch: function(thrown, throwntype, typeArray) { + // 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. + if (throwntype != 0) { + var throwntypeInfoAddr= {{{ makeGetValue('throwntype', '0', '*') }}} - {{{ Runtime.QUANTUM_SIZE*2 }}}; + var throwntypeInfo= {{{ makeGetValue('throwntypeInfoAddr', '0', '*') }}}; + if (throwntypeInfo == 0) + thrown = {{{ makeGetValue('thrown', '0', '*') }}}; + } + // 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 (___cxa_does_inherit(typeArray[i], throwntype)) + return { 'f0':thrown, 'f1':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. + return {'f0':thrown,'f1':throwntype}; + }, + + // Recursively walks up the base types of 'possibilityType' + // to see if any of them match 'definiteType'. + + __cxa_does_inherit: function(definiteType, possibilityType) { + if (possibilityType == 0 || possibilityType == definiteType) + return true; + var possibility_type_infoAddr = {{{ makeGetValue('possibilityType', '0', '*') }}} - {{{ Runtime.QUANTUM_SIZE*2 }}}; + var possibility_type_info = {{{ makeGetValue('possibility_type_infoAddr', '0', '*') }}}; + switch (possibility_type_info) { + case 0: // possibility is a pointer + // See if definite type is a pointer + var definite_type_infoAddr = {{{ makeGetValue('definiteType', '0', '*') }}} - {{{ Runtime.QUANTUM_SIZE*2 }}}; + var definite_type_info = {{{ makeGetValue('definite_type_infoAddr', '0', '*') }}}; + if (definite_type_info == 0) { + // Also a pointer; compare base types of pointers + var defPointerBaseAddr = definiteType+{{{ Runtime.QUANTUM_SIZE*2 }}}; + var defPointerBaseType = {{{ makeGetValue('defPointerBaseAddr', '0', '*') }}}; + var possPointerBaseAddr = possibilityType+{{{ Runtime.QUANTUM_SIZE*2 }}}; + var possPointerBaseType = {{{ makeGetValue('possPointerBaseAddr', '0', '*') }}}; + return ___cxa_does_inherit(defPointerBaseType, possPointerBaseType); + } else + return false; // one pointer and one non-pointer + case 1: // class with no base class + return false; + case 2: // class with base class + var parentTypeAddr = possibilityType + {{{ Runtime.QUANTUM_SIZE*2 }}}; + var parentType = {{{ makeGetValue('parentTypeAddr', '0', '*') }}}; + return ___cxa_does_inherit(definiteType, parentType); + default: + return false; // some unencountered type + } + }, + // 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. @@ -4503,6 +4572,8 @@ LibraryManager.library = { _ZTIc: [0], // type_info for void. _ZTIv: [0], + // type_info for void*. + _ZTIPv: [0], llvm_uadd_with_overflow_i32: function(x, y) { return { diff --git a/src/parseTools.js b/src/parseTools.js index 1b48737e..2ab43ebf 100644 --- a/src/parseTools.js +++ b/src/parseTools.js @@ -227,7 +227,11 @@ function getTokenIndexByText(tokens, text) { } function findTokenText(item, text) { - for (var i = 0; i < item.tokens.length; i++) { + return findTokenTextAfter(item, text, 0); +} + +function findTokenTextAfter(item, text, startAt) { + for (var i = startAt; i < item.tokens.length; i++) { if (item.tokens[i].text == text) return i; } return -1; |