Skip to content

Commit 55c718a

Browse files
authored
Merge pull request #1084 from nevkontakte/double-defer
Use object identity to detect how far to unwind stack after panic recovery.
2 parents 201d64d + 61e9bf8 commit 55c718a

File tree

5 files changed

+89
-4
lines changed

5 files changed

+89
-4
lines changed

compiler/package.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -831,7 +831,7 @@ func translateFunction(typ *ast.FuncType, recv *ast.Ident, body *ast.BlockStmt,
831831
}
832832

833833
if c.HasDefer {
834-
prefix = prefix + " $deferred = []; $deferred.index = $curGoroutine.deferStack.length; $curGoroutine.deferStack.push($deferred);"
834+
prefix = prefix + " $deferred = []; $curGoroutine.deferStack.push($deferred);"
835835
}
836836

837837
if prefix != "" {

compiler/prelude/goroutines.go

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ var $getStackDepth = function() {
1212
1313
var $panicStackDepth = null, $panicValue;
1414
var $callDeferred = function(deferred, jsErr, fromPanic) {
15-
if (!fromPanic && deferred !== null && deferred.index >= $curGoroutine.deferStack.length) {
15+
if (!fromPanic && deferred !== null && $curGoroutine.deferStack.indexOf(deferred) == -1) {
1616
throw jsErr;
1717
}
1818
if (jsErr !== null) {
@@ -88,6 +88,18 @@ var $callDeferred = function(deferred, jsErr, fromPanic) {
8888
return;
8989
}
9090
}
91+
} catch(e) {
92+
// Deferred function threw a JavaScript exception or tries to unwind stack
93+
// to the point where a panic was handled.
94+
if (fromPanic) {
95+
// Re-throw the exception to reach deferral execution call at the end
96+
// of the function.
97+
throw e;
98+
}
99+
// We are at the end of the function, handle the error or re-throw to
100+
// continue unwinding if necessary, or simply stop unwinding if we got far
101+
// enough.
102+
$callDeferred(deferred, e, fromPanic);
91103
} finally {
92104
if (localPanicValue !== undefined) {
93105
if ($panicStackDepth !== null) {

compiler/prelude/prelude_min.go

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/deferblock_test.go

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package tests
22

33
import (
4+
"errors"
5+
"fmt"
46
"testing"
57
"time"
68
)
@@ -40,3 +42,75 @@ func TestBlockingInDefer(t *testing.T) {
4042

4143
outer(ch, b)
4244
}
45+
46+
func TestIssue1083(t *testing.T) {
47+
// https://github.com/gopherjs/gopherjs/issues/1083
48+
var block = make(chan bool)
49+
50+
recoverCompleted := false
51+
52+
recoverAndBlock := func() {
53+
defer func() {}()
54+
recover()
55+
block <- true
56+
recoverCompleted = true
57+
}
58+
59+
handle := func() {
60+
defer func() {}()
61+
panic("expected panic")
62+
}
63+
64+
serve := func() {
65+
defer recoverAndBlock()
66+
handle()
67+
t.Fatal("This line must never execute.")
68+
}
69+
70+
go func() { <-block }()
71+
72+
serve()
73+
if !recoverCompleted {
74+
t.Fatal("Recovery function did not execute fully.")
75+
}
76+
}
77+
78+
func TestIssue780(t *testing.T) {
79+
// https://github.com/gopherjs/gopherjs/issues/780
80+
want := errors.New("expected error")
81+
var got error
82+
83+
catch := func() {
84+
if r := recover(); r != nil {
85+
got = r.(error)
86+
}
87+
}
88+
throw := func() { panic(want) }
89+
90+
catchAndThrow := func() {
91+
t.Logf("catchAndThrow: %v", recover())
92+
panic(want)
93+
}
94+
95+
execute := func(x int) (err error) {
96+
defer catch() // Final recovery.
97+
98+
for i := 0; i < x; i++ {
99+
// Test that several deferred panics can be handled.
100+
defer catchAndThrow()
101+
}
102+
103+
defer throw() // Emulates a panicing cleanup.
104+
105+
return nil
106+
}
107+
108+
for _, x := range []int{0, 1, 2, 5, 10} {
109+
t.Run(fmt.Sprint(x), func(t *testing.T) {
110+
execute(x)
111+
if !errors.Is(got, want) {
112+
t.Errorf("process() returned error %v, want %v", got, want)
113+
}
114+
})
115+
}
116+
}

tests/gorepo/run.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,6 @@ var knownFails = map[string]failReason{
6868
"fixedbugs/issue6899.go": {desc: "incorrect output -0"},
6969
"fixedbugs/issue7550.go": {category: neverTerminates, desc: "FATAL ERROR: invalid table size Allocation failed - process out of memory"},
7070
"fixedbugs/issue7690.go": {desc: "Error: runtime error: slice bounds out of range"},
71-
"fixedbugs/issue8047.go": {desc: "null"},
7271
"fixedbugs/issue8047b.go": {desc: "Error: [object Object]"},
7372

7473
// Failing due to use of os/exec.Command, which is unsupported. Now skipped via !nacl build tag.

0 commit comments

Comments
 (0)