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:
Родитель
1d5093e312
Коммит
e6f092d69f
|
@ -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()
|
||||
|
|
Загрузка…
Ссылка в новой задаче