}
}
+func TestRecursivePanic(t *testing.T) {
+ output := executeTest(t, recursivePanicSource, nil)
+ want := `wrap: bad
+panic: again
+
+`
+ if !strings.HasPrefix(output, want) {
+ t.Fatalf("output does not start with %q:\n%s", want, output)
+ }
+
+}
+
const crashSource = `
package main
}
}
`
+
+const recursivePanicSource = `
+package main
+
+import (
+ "fmt"
+)
+
+func main() {
+ func() {
+ defer func() {
+ fmt.Println(recover())
+ }()
+ var x [8192]byte
+ func(x [8192]byte) {
+ defer func() {
+ if err := recover(); err != nil {
+ panic("wrap: " + err.(string))
+ }
+ }()
+ panic("bad")
+ }(x)
+ }()
+ panic("again")
+}
+`
}
static void recovery(G*);
+static void abortpanic(Panic*);
+static FuncVal abortpanicV = { (void(*)(void))abortpanic };
// The implementation of the predeclared function panic.
void
runtime·panic(Eface e)
{
- Defer *d;
+ Defer *d, dabort;
Panic p;
void *pc, *argp;
p.stackbase = g->stackbase;
g->panic = &p;
+ dabort.fn = &abortpanicV;
+ dabort.siz = sizeof(&p);
+ dabort.args[0] = &p;
+ dabort.argp = (void*)-1; // unused because abortpanic never recovers
+ dabort.special = true;
+
for(;;) {
d = g->defer;
if(d == nil)
g->ispanic = true; // rock for runtime·newstack, where runtime·newstackcall ends up
argp = d->argp;
pc = d->pc;
+
+ // The deferred function may cause another panic,
+ // so newstackcall may not return. Set up a defer
+ // to mark this panic aborted if that happens.
+ dabort.link = g->defer;
+ g->defer = &dabort;
+ p.defer = d;
+
runtime·newstackcall(d->fn, (byte*)d->args, d->siz);
+
+ // Newstackcall did not panic. Remove dabort.
+ if(g->defer != &dabort)
+ runtime·throw("bad defer entry in panic");
+ g->defer = dabort.link;
+
freedefer(d);
if(p.recovered) {
g->panic = p.link;
+ // Aborted panics are marked but remain on the g->panic list.
+ // Recovery will unwind the stack frames containing their Panic structs.
+ // Remove them from the list and free the associated defers.
+ while(g->panic && g->panic->aborted) {
+ freedefer(g->panic->defer);
+ g->panic = g->panic->link;
+ }
if(g->panic == nil) // must be done with signal
g->sig = 0;
// Pass information about recovering frame to recovery.
runtime·exit(1); // not reached
}
+static void
+abortpanic(Panic *p)
+{
+ p->aborted = true;
+}
+
// Unwind the stack after a deferred function calls recover
// after a panic. Then arrange to continue running as though
// the caller of the deferred function returned normally.
Eface arg; // argument to panic
uintptr stackbase; // g->stackbase in panic
Panic* link; // link to earlier panic
+ Defer* defer; // current executing defer
bool recovered; // whether this panic is over
+ bool aborted; // the panic was aborted
};
/*