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.
This commit is contained in:
Adrian Taylor 2012-02-10 23:11:34 +00:00
Родитель 1d5093e312
Коммит e6f092d69f
7 изменённых файлов: 141 добавлений и 30 удалений

Просмотреть файл

@ -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');
}

Просмотреть файл

@ -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);

Просмотреть файл

@ -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 {

Просмотреть файл

@ -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;

Просмотреть файл

@ -1,9 +1,12 @@
*CREATING A FOO
*CREATING A BAR
*CREATING A QUUX
*CREATING A QUUX
*CREATING A CHILD
start
test 0
throwing ExFooInstance
*COPYING A FOO
*COPYING A FOO
@ -12,6 +15,7 @@ outer catch foo: 11
*DESTROYING A FOO (11)
test 1
throwing ExBarInstance
*COPYING A BAR
*COPYING A BAR
@ -21,6 +25,7 @@ outer catch bar-ref: 22
*DESTROYING A BAR (22)
test 2
throwing ExQuuxInstance
*COPYING A QUUX
*COPYING A QUUX
@ -29,22 +34,44 @@ inner catch quux: 33
*DESTROYING A QUUX (33)
test 3
throwing ExQuux ptr
outer catch quux-ptr: 33
test 4
throwing ExChildInstance
*CREATING A QUUX
*COPYING CHILD
*COPYING A QUUX
inner catch quux: 44
*DESTROYING A QUUX (44)
*DESTROYING A CHILD (44)
*DESTROYING A QUUX (44)
test 5
throwing ExChildInstance ptr
outer catch quux-ptr: 44
test 6
throwing 42
outer catch int: 42
test 7
throwing NULL
outer catch-all
test 8
not throwing
end
*DESTROYING A CHILD (44)
*DESTROYING A QUUX (44)
*DESTROYING A QUUX (33)
*DESTROYING A BAR (22)
*DESTROYING A FOO (11)

Просмотреть файл

@ -21,13 +21,12 @@ public:
ExQuux(const ExQuux& other) { x=other.x; printf("*COPYING A QUUX\n"); }
~ExQuux() { printf("*DESTROYING A QUUX (%d)\n", x); }
} ExQuuxInstance(33);
// NOTE: Throwing pointers and polymorphic matching not supported.
// class ExChild : public ExQuux {
// public:
// ExChild(int x) : ExQuux(x) { printf("*CREATING A CHILD\n"); }
// ExChild(const ExChild& other) : ExQuux(x) { x=other.x; printf("*COPYING CHILD\n"); }
// ~ExChild() { printf("*DESTROYING A CHILD (%d)\n", x); }
// } ExChildInstance(44);
class ExChild : public ExQuux {
public:
ExChild(int x) : ExQuux(x) { printf("*CREATING A CHILD\n"); }
ExChild(const ExChild& other) : ExQuux(x) { x=other.x; printf("*COPYING CHILD\n"); }
~ExChild() { printf("*DESTROYING A CHILD (%d)\n", x); }
} ExChildInstance(44);
void magic(int which) {
try {
@ -41,20 +40,22 @@ void magic(int which) {
case 2:
printf(" throwing ExQuuxInstance\n");
throw ExQuuxInstance;
// NOTE: Throwing pointers and polymorphic matching not supported.
// case 3:
// printf(" throwing ExQuux ptr\n");
// throw &ExQuuxInstance;
// case 4:
// printf(" throwing ExChildInstance\n");
// throw ExChildInstance;
case 3:
printf(" throwing ExQuux ptr\n");
throw &ExQuuxInstance;
case 4:
printf(" throwing ExChildInstance\n");
throw ExChildInstance;
case 5:
printf(" throwing ExChildInstance ptr\n");
throw &ExChildInstance;
case 6:
printf(" throwing 42\n");
throw 42;
case 6:
case 7:
printf(" throwing NULL\n");
throw (void*)0;
case 7:
case 8:
printf(" not throwing\n");
}
} catch (ExQuux e1) {
@ -67,18 +68,18 @@ void magic(int which) {
int main() {
printf("start\n\n\n");
for (int i = 0; i < 8; i++) {
for (int i = 0; i < 9; i++) {
printf("test %d\n", i);
try {
magic(i);
} catch (ExFoo e1) {
printf("outer catch foo: %d\n", e1.x);
} catch (ExBar& e2) {
printf("outer catch bar-ref: %d\n", e2.x);
// NOTE: Throwing pointers and polymorphic matching not supported.
// } catch (ExQuux& e3) {
// printf("outer catch quux-ref: %d\n", e3.x);
// } catch (ExQuux* e4) {
// printf("outer catch quux-ptr: %d\n", e4->x);
} catch (ExQuux& e3) {
printf("outer catch quux-ref: %d\n", e3.x);
} catch (ExQuux* e4) {
printf("outer catch quux-ptr: %d\n", e4->x);
} catch (int e5) {
printf("outer catch int: %d\n", e5);
} catch (...) {

Просмотреть файл

@ -1446,8 +1446,6 @@ if 'benchmark' not in str(sys.argv) and 'sanity' not in str(sys.argv):
self.do_run(src, 'success')
def test_typed_exceptions(self):
return self.skip('TODO: fix this for llvm 3.0')
Settings.SAFE_HEAP = 0 # Throwing null will cause an ignorable null pointer access.
Settings.EXCEPTION_DEBUG = 0 # Messes up expected output.
src = open(path_from_root('tests', 'exceptions', 'typed.cpp'), 'r').read()