diff options
-rw-r--r-- | src/intertyper.js | 1 | ||||
-rw-r--r-- | src/jsifier.js | 8 | ||||
-rw-r--r-- | src/library.js | 4 | ||||
-rw-r--r-- | src/preamble.js | 2 | ||||
-rwxr-xr-x | tests/runner.py | 166 |
5 files changed, 176 insertions, 5 deletions
diff --git a/src/intertyper.js b/src/intertyper.js index f63e0982..2de6c6b9 100644 --- a/src/intertyper.js +++ b/src/intertyper.js @@ -926,6 +926,7 @@ function intertyper(data, sidePass, baseLineNums) { processItem: function(item) { return [{ intertype: 'resume', + ident: toNiceIdent(item.tokens[2].text), lineNum: item.lineNum }]; } diff --git a/src/jsifier.js b/src/jsifier.js index a6a3fb28..0e8b825a 100644 --- a/src/jsifier.js +++ b/src/jsifier.js @@ -664,7 +664,7 @@ function JSify(data, functionsOnly, givenFunctions) { } // otherwise, should have been set before! if (func.setjmpTable) { var setjmpTable = {}; - ret += indent + 'var setjmped = false;'; // set to true if we setjmp in this invocation + ret += indent + 'var mySetjmpIds = {};\n'; ret += indent + 'var setjmpTable = {'; func.setjmpTable.forEach(function(triple) { // original label, label we created for right after the setjmp, variable setjmp result goes into ret += '"' + getLabelId(triple[0]) + '": ' + 'function(value) { label = ' + getLabelId(triple[1]) + '; ' + triple[2] + ' = value },'; @@ -683,7 +683,7 @@ function JSify(data, functionsOnly, givenFunctions) { }).join('\n'); ret += '\n' + indent + ' default: assert(0, "bad label: " + label);\n' + indent + '}'; if (func.setjmpTable) { - ret += ' } catch(e) { if (!setjmped) throw(e); if (!e.longjmp) throw(e); setjmpTable[e.label](e.value) }'; + ret += ' } catch(e) { if (!e.longjmp || !(e.id in mySetjmpIds)) throw(e); setjmpTable[setjmpLabels[e.id]](e.value) }'; } } else { ret += (SHOW_LABELS ? indent + '/* ' + block.entries[0] + ' */' : '') + '\n' + getLabelLines(block.labels[0], indent); @@ -1081,8 +1081,10 @@ function JSify(data, functionsOnly, givenFunctions) { return ret + ';'; }); makeFuncLineActor('resume', function(item) { + // If there is no current exception, set this one as it (during a resume, the current exception can be wiped out) return (EXCEPTION_DEBUG ? 'Module.print("Resuming exception");' : '') + - 'throw ' + makeGetValue('_llvm_eh_exception.buf', '0', 'void*') + ';'; + 'if (' + makeGetValue('_llvm_eh_exception.buf', 0, 'void*') + ' == 0) { ' + makeSetValue('_llvm_eh_exception.buf', 0, item.ident + '.f0', 'void*') + ' } ' + + 'throw ' + item.ident + '.f0;'; }); makeFuncLineActor('invoke', function(item) { // Wrapping in a function lets us easily return values if we are diff --git a/src/library.js b/src/library.js index f23e87f7..5834797f 100644 --- a/src/library.js +++ b/src/library.js @@ -5922,11 +5922,11 @@ LibraryManager.library = { setjmp__inline: function(env) { // Save the label - return '(setjmped = true, ' + makeSetValue(env, '0', 'label', 'i32') + ', 0)'; + return '(tempInt = setjmpId++, mySetjmpIds[tempInt] = 1, setjmpLabels[tempInt] = label,' + makeSetValue(env, '0', 'tempInt', 'i32') + ', 0)'; }, longjmp: function(env, value) { - throw { longjmp: true, label: {{{ makeGetValue('env', '0', 'i32') }}}, value: value || 1 }; + throw { longjmp: true, id: {{{ makeGetValue('env', '0', 'i32') }}}, value: value || 1 }; }, // ========================================================================== diff --git a/src/preamble.js b/src/preamble.js index ae264a90..b28dfc0d 100644 --- a/src/preamble.js +++ b/src/preamble.js @@ -276,6 +276,8 @@ Module['printProfiling'] = printProfiling; //======================================== var __THREW__ = 0; // Used in checking for thrown exceptions. +var setjmpId = 1; // Used in setjmp/longjmp +var setjmpLabels = {}; var ABORT = false; diff --git a/tests/runner.py b/tests/runner.py index 8bf71758..a231cb5c 100755 --- a/tests/runner.py +++ b/tests/runner.py @@ -2087,6 +2087,109 @@ Setjmp error execution path, level: 0 Exiting stack_manipulate_func, level: 0 ''') + def test_longjmp3(self): + src = r''' + #include <setjmp.h> + #include <stdio.h> + + typedef struct { + jmp_buf* jmp; + } jmp_state; + + void setjmp_func(jmp_state* s, int level) { + jmp_buf* prev_jmp = s->jmp; + jmp_buf c_jmp; + + if (level == 2) { + printf("level is 2, perform longjmp!\n"); + longjmp(*(s->jmp), 1); + } + + if (setjmp(c_jmp) == 0) { + printf("setjmp normal execution path, level: %d\n", level); + s->jmp = &c_jmp; + setjmp_func(s, level + 1); + } else { + printf("setjmp exception execution path, level: %d\n", level); + if (prev_jmp) { + printf("prev_jmp is not empty, continue with longjmp!\n"); + s->jmp = prev_jmp; + longjmp(*(s->jmp), 1); + } + } + + printf("Exiting setjmp function, level: %d\n", level); + } + + int main(int argc, char *argv[]) { + jmp_state s; + s.jmp = NULL; + + setjmp_func(&s, 0); + + return 0; + } + ''' + self.do_run(src, '''setjmp normal execution path, level: 0 +setjmp normal execution path, level: 1 +level is 2, perform longjmp! +setjmp exception execution path, level: 1 +prev_jmp is not empty, continue with longjmp! +setjmp exception execution path, level: 0 +Exiting setjmp function, level: 0 +''') + + def test_longjmp4(self): + src = r''' + #include <setjmp.h> + #include <stdio.h> + + typedef struct { + jmp_buf* jmp; + } jmp_state; + + void second_func(jmp_state* s); + + void first_func(jmp_state* s) { + jmp_buf* prev_jmp = s->jmp; + jmp_buf c_jmp; + volatile int once = 0; + + if (setjmp(c_jmp) == 0) { + printf("Normal execution path of first function!\n"); + + s->jmp = &c_jmp; + second_func(s); + } else { + printf("Exception execution path of first function! %d\n", once); + + if (!once) { + printf("Calling longjmp the second time!\n"); + once = 1; + longjmp(*(s->jmp), 1); + } + } + } + + void second_func(jmp_state* s) { + longjmp(*(s->jmp), 1); + } + + int main(int argc, char *argv[]) { + jmp_state s; + s.jmp = NULL; + + first_func(&s); + + return 0; + } + ''' + self.do_run(src, '''Normal execution path of first function! +Exception execution path of first function! 0 +Calling longjmp the second time! +Exception execution path of first function! 1 +''') + def test_exceptions(self): if Settings.QUANTUM_SIZE == 1: return self.skip("we don't support libcxx in q1") @@ -2226,6 +2329,69 @@ Exiting stack_manipulate_func, level: 0 expected = open(path_from_root('tests', 'exceptions', 'output.txt'), 'r').read() self.do_run(src, expected) + def test_multiexception(self): + Settings.DISABLE_EXCEPTION_CATCHING = 0 + Settings.EXCEPTION_DEBUG = 0 # Messes up expected output. + src = r''' +#include <stdio.h> + +static int current_exception_id = 0; + +typedef struct { + int jmp; +} jmp_state; + +void setjmp_func(jmp_state* s, int level) { + int prev_jmp = s->jmp; + int c_jmp; + + if (level == 2) { + printf("level is 2, perform longjmp!\n"); + throw 1; + } + + c_jmp = current_exception_id++; + try { + printf("setjmp normal execution path, level: %d, prev_jmp: %d\n", level, prev_jmp); + s->jmp = c_jmp; + setjmp_func(s, level + 1); + } catch (int catched_eid) { + printf("caught %d\n", catched_eid); + if (catched_eid == c_jmp) { + printf("setjmp exception execution path, level: %d, prev_jmp: %d\n", level, prev_jmp); + if (prev_jmp != -1) { + printf("prev_jmp is not empty, continue with longjmp!\n"); + s->jmp = prev_jmp; + throw s->jmp; + } + } else { + throw; + } + } + + printf("Exiting setjmp function, level: %d, prev_jmp: %d\n", level, prev_jmp); +} + +int main(int argc, char *argv[]) { + jmp_state s; + s.jmp = -1; + + setjmp_func(&s, 0); + + return 0; +} +''' + self.do_run(src, '''setjmp normal execution path, level: 0, prev_jmp: -1 +setjmp normal execution path, level: 1, prev_jmp: 0 +level is 2, perform longjmp! +caught 1 +setjmp exception execution path, level: 1, prev_jmp: 0 +prev_jmp is not empty, continue with longjmp! +caught 0 +setjmp exception execution path, level: 0, prev_jmp: -1 +Exiting setjmp function, level: 0, prev_jmp: -1 +''') + def test_class(self): src = ''' #include <stdio.h> |