2012-02-16 21:51:04 -07:00
|
|
|
// run
|
2010-03-31 12:47:09 -06:00
|
|
|
|
|
|
|
// Copyright 2010 The Go Authors. All rights reserved.
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
|
|
// Test of basic recover functionality.
|
|
|
|
|
|
|
|
package main
|
|
|
|
|
2013-02-21 10:48:38 -07:00
|
|
|
import (
|
|
|
|
"os"
|
runtime, cmd/gc, cmd/ld: ignore method wrappers in recover
Bug #1:
Issue 5406 identified an interesting case:
defer iface.M()
may end up calling a wrapper that copies an indirect receiver
from the iface value and then calls the real M method. That's
two calls down, not just one, and so recover() == nil always
in the real M method, even during a panic.
[For the purposes of this entire discussion, a wrapper's
implementation is a function containing an ordinary call, not
the optimized tail call form that is somtimes possible. The
tail call does not create a second frame, so it is already
handled correctly.]
Fix this bug by introducing g->panicwrap, which counts the
number of bytes on current stack segment that are due to
wrapper calls that should not count against the recover
check. All wrapper functions must now adjust g->panicwrap up
on entry and back down on exit. This adds slightly to their
expense; on the x86 it is a single instruction at entry and
exit; on the ARM it is three. However, the alternative is to
make a call to recover depend on being able to walk the stack,
which I very much want to avoid. We have enough problems
walking the stack for garbage collection and profiling.
Also, if performance is critical in a specific case, it is already
faster to use a pointer receiver and avoid this kind of wrapper
entirely.
Bug #2:
The old code, which did not consider the possibility of two
calls, already contained a check to see if the call had split
its stack and so the panic-created segment was one behind the
current segment. In the wrapper case, both of the two calls
might split their stacks, so the panic-created segment can be
two behind the current segment.
Fix this by propagating the Stktop.panic flag forward during
stack splits instead of looking backward during recover.
Fixes #5406.
R=golang-dev, iant
CC=golang-dev
https://golang.org/cl/13367052
2013-09-12 12:00:16 -06:00
|
|
|
"reflect"
|
2013-02-21 10:48:38 -07:00
|
|
|
"runtime"
|
|
|
|
)
|
2010-03-31 12:47:09 -06:00
|
|
|
|
|
|
|
func main() {
|
2013-09-18 12:44:57 -06:00
|
|
|
// go.tools/ssa/interp still has:
|
|
|
|
// - some lesser bugs in recover()
|
|
|
|
// - incomplete support for reflection
|
|
|
|
interp := os.Getenv("GOSSAINTERP") != ""
|
|
|
|
|
2010-03-31 12:47:09 -06:00
|
|
|
test1()
|
|
|
|
test1WithClosures()
|
|
|
|
test2()
|
|
|
|
test3()
|
2013-09-18 12:44:57 -06:00
|
|
|
if !interp {
|
2013-02-21 10:48:38 -07:00
|
|
|
test4()
|
|
|
|
}
|
2013-09-18 12:44:57 -06:00
|
|
|
test5()
|
2010-03-31 12:47:09 -06:00
|
|
|
test6()
|
|
|
|
test6WithClosures()
|
|
|
|
test7()
|
runtime, cmd/gc, cmd/ld: ignore method wrappers in recover
Bug #1:
Issue 5406 identified an interesting case:
defer iface.M()
may end up calling a wrapper that copies an indirect receiver
from the iface value and then calls the real M method. That's
two calls down, not just one, and so recover() == nil always
in the real M method, even during a panic.
[For the purposes of this entire discussion, a wrapper's
implementation is a function containing an ordinary call, not
the optimized tail call form that is somtimes possible. The
tail call does not create a second frame, so it is already
handled correctly.]
Fix this bug by introducing g->panicwrap, which counts the
number of bytes on current stack segment that are due to
wrapper calls that should not count against the recover
check. All wrapper functions must now adjust g->panicwrap up
on entry and back down on exit. This adds slightly to their
expense; on the x86 it is a single instruction at entry and
exit; on the ARM it is three. However, the alternative is to
make a call to recover depend on being able to walk the stack,
which I very much want to avoid. We have enough problems
walking the stack for garbage collection and profiling.
Also, if performance is critical in a specific case, it is already
faster to use a pointer receiver and avoid this kind of wrapper
entirely.
Bug #2:
The old code, which did not consider the possibility of two
calls, already contained a check to see if the call had split
its stack and so the panic-created segment was one behind the
current segment. In the wrapper case, both of the two calls
might split their stacks, so the panic-created segment can be
two behind the current segment.
Fix this by propagating the Stktop.panic flag forward during
stack splits instead of looking backward during recover.
Fixes #5406.
R=golang-dev, iant
CC=golang-dev
https://golang.org/cl/13367052
2013-09-12 12:00:16 -06:00
|
|
|
test8()
|
|
|
|
test9()
|
2013-09-18 12:44:57 -06:00
|
|
|
if !interp {
|
|
|
|
test9reflect1()
|
|
|
|
test9reflect2()
|
|
|
|
}
|
runtime, cmd/gc, cmd/ld: ignore method wrappers in recover
Bug #1:
Issue 5406 identified an interesting case:
defer iface.M()
may end up calling a wrapper that copies an indirect receiver
from the iface value and then calls the real M method. That's
two calls down, not just one, and so recover() == nil always
in the real M method, even during a panic.
[For the purposes of this entire discussion, a wrapper's
implementation is a function containing an ordinary call, not
the optimized tail call form that is somtimes possible. The
tail call does not create a second frame, so it is already
handled correctly.]
Fix this bug by introducing g->panicwrap, which counts the
number of bytes on current stack segment that are due to
wrapper calls that should not count against the recover
check. All wrapper functions must now adjust g->panicwrap up
on entry and back down on exit. This adds slightly to their
expense; on the x86 it is a single instruction at entry and
exit; on the ARM it is three. However, the alternative is to
make a call to recover depend on being able to walk the stack,
which I very much want to avoid. We have enough problems
walking the stack for garbage collection and profiling.
Also, if performance is critical in a specific case, it is already
faster to use a pointer receiver and avoid this kind of wrapper
entirely.
Bug #2:
The old code, which did not consider the possibility of two
calls, already contained a check to see if the call had split
its stack and so the panic-created segment was one behind the
current segment. In the wrapper case, both of the two calls
might split their stacks, so the panic-created segment can be
two behind the current segment.
Fix this by propagating the Stktop.panic flag forward during
stack splits instead of looking backward during recover.
Fixes #5406.
R=golang-dev, iant
CC=golang-dev
https://golang.org/cl/13367052
2013-09-12 12:00:16 -06:00
|
|
|
test10()
|
2013-09-18 12:44:57 -06:00
|
|
|
if !interp {
|
|
|
|
test10reflect1()
|
|
|
|
test10reflect2()
|
|
|
|
}
|
runtime, cmd/gc, cmd/ld: ignore method wrappers in recover
Bug #1:
Issue 5406 identified an interesting case:
defer iface.M()
may end up calling a wrapper that copies an indirect receiver
from the iface value and then calls the real M method. That's
two calls down, not just one, and so recover() == nil always
in the real M method, even during a panic.
[For the purposes of this entire discussion, a wrapper's
implementation is a function containing an ordinary call, not
the optimized tail call form that is somtimes possible. The
tail call does not create a second frame, so it is already
handled correctly.]
Fix this bug by introducing g->panicwrap, which counts the
number of bytes on current stack segment that are due to
wrapper calls that should not count against the recover
check. All wrapper functions must now adjust g->panicwrap up
on entry and back down on exit. This adds slightly to their
expense; on the x86 it is a single instruction at entry and
exit; on the ARM it is three. However, the alternative is to
make a call to recover depend on being able to walk the stack,
which I very much want to avoid. We have enough problems
walking the stack for garbage collection and profiling.
Also, if performance is critical in a specific case, it is already
faster to use a pointer receiver and avoid this kind of wrapper
entirely.
Bug #2:
The old code, which did not consider the possibility of two
calls, already contained a check to see if the call had split
its stack and so the panic-created segment was one behind the
current segment. In the wrapper case, both of the two calls
might split their stacks, so the panic-created segment can be
two behind the current segment.
Fix this by propagating the Stktop.panic flag forward during
stack splits instead of looking backward during recover.
Fixes #5406.
R=golang-dev, iant
CC=golang-dev
https://golang.org/cl/13367052
2013-09-12 12:00:16 -06:00
|
|
|
test11()
|
2013-09-18 12:44:57 -06:00
|
|
|
if !interp {
|
|
|
|
test11reflect1()
|
|
|
|
test11reflect2()
|
|
|
|
}
|
2014-09-06 11:19:08 -06:00
|
|
|
test111()
|
runtime, cmd/gc, cmd/ld: ignore method wrappers in recover
Bug #1:
Issue 5406 identified an interesting case:
defer iface.M()
may end up calling a wrapper that copies an indirect receiver
from the iface value and then calls the real M method. That's
two calls down, not just one, and so recover() == nil always
in the real M method, even during a panic.
[For the purposes of this entire discussion, a wrapper's
implementation is a function containing an ordinary call, not
the optimized tail call form that is somtimes possible. The
tail call does not create a second frame, so it is already
handled correctly.]
Fix this bug by introducing g->panicwrap, which counts the
number of bytes on current stack segment that are due to
wrapper calls that should not count against the recover
check. All wrapper functions must now adjust g->panicwrap up
on entry and back down on exit. This adds slightly to their
expense; on the x86 it is a single instruction at entry and
exit; on the ARM it is three. However, the alternative is to
make a call to recover depend on being able to walk the stack,
which I very much want to avoid. We have enough problems
walking the stack for garbage collection and profiling.
Also, if performance is critical in a specific case, it is already
faster to use a pointer receiver and avoid this kind of wrapper
entirely.
Bug #2:
The old code, which did not consider the possibility of two
calls, already contained a check to see if the call had split
its stack and so the panic-created segment was one behind the
current segment. In the wrapper case, both of the two calls
might split their stacks, so the panic-created segment can be
two behind the current segment.
Fix this by propagating the Stktop.panic flag forward during
stack splits instead of looking backward during recover.
Fixes #5406.
R=golang-dev, iant
CC=golang-dev
https://golang.org/cl/13367052
2013-09-12 12:00:16 -06:00
|
|
|
test12()
|
2013-09-18 12:44:57 -06:00
|
|
|
if !interp {
|
|
|
|
test12reflect1()
|
|
|
|
test12reflect2()
|
|
|
|
}
|
runtime, cmd/gc, cmd/ld: ignore method wrappers in recover
Bug #1:
Issue 5406 identified an interesting case:
defer iface.M()
may end up calling a wrapper that copies an indirect receiver
from the iface value and then calls the real M method. That's
two calls down, not just one, and so recover() == nil always
in the real M method, even during a panic.
[For the purposes of this entire discussion, a wrapper's
implementation is a function containing an ordinary call, not
the optimized tail call form that is somtimes possible. The
tail call does not create a second frame, so it is already
handled correctly.]
Fix this bug by introducing g->panicwrap, which counts the
number of bytes on current stack segment that are due to
wrapper calls that should not count against the recover
check. All wrapper functions must now adjust g->panicwrap up
on entry and back down on exit. This adds slightly to their
expense; on the x86 it is a single instruction at entry and
exit; on the ARM it is three. However, the alternative is to
make a call to recover depend on being able to walk the stack,
which I very much want to avoid. We have enough problems
walking the stack for garbage collection and profiling.
Also, if performance is critical in a specific case, it is already
faster to use a pointer receiver and avoid this kind of wrapper
entirely.
Bug #2:
The old code, which did not consider the possibility of two
calls, already contained a check to see if the call had split
its stack and so the panic-created segment was one behind the
current segment. In the wrapper case, both of the two calls
might split their stacks, so the panic-created segment can be
two behind the current segment.
Fix this by propagating the Stktop.panic flag forward during
stack splits instead of looking backward during recover.
Fixes #5406.
R=golang-dev, iant
CC=golang-dev
https://golang.org/cl/13367052
2013-09-12 12:00:16 -06:00
|
|
|
test13()
|
2013-09-18 12:44:57 -06:00
|
|
|
if !interp {
|
|
|
|
test13reflect1()
|
|
|
|
test13reflect2()
|
|
|
|
}
|
runtime, cmd/gc, cmd/ld: ignore method wrappers in recover
Bug #1:
Issue 5406 identified an interesting case:
defer iface.M()
may end up calling a wrapper that copies an indirect receiver
from the iface value and then calls the real M method. That's
two calls down, not just one, and so recover() == nil always
in the real M method, even during a panic.
[For the purposes of this entire discussion, a wrapper's
implementation is a function containing an ordinary call, not
the optimized tail call form that is somtimes possible. The
tail call does not create a second frame, so it is already
handled correctly.]
Fix this bug by introducing g->panicwrap, which counts the
number of bytes on current stack segment that are due to
wrapper calls that should not count against the recover
check. All wrapper functions must now adjust g->panicwrap up
on entry and back down on exit. This adds slightly to their
expense; on the x86 it is a single instruction at entry and
exit; on the ARM it is three. However, the alternative is to
make a call to recover depend on being able to walk the stack,
which I very much want to avoid. We have enough problems
walking the stack for garbage collection and profiling.
Also, if performance is critical in a specific case, it is already
faster to use a pointer receiver and avoid this kind of wrapper
entirely.
Bug #2:
The old code, which did not consider the possibility of two
calls, already contained a check to see if the call had split
its stack and so the panic-created segment was one behind the
current segment. In the wrapper case, both of the two calls
might split their stacks, so the panic-created segment can be
two behind the current segment.
Fix this by propagating the Stktop.panic flag forward during
stack splits instead of looking backward during recover.
Fixes #5406.
R=golang-dev, iant
CC=golang-dev
https://golang.org/cl/13367052
2013-09-12 12:00:16 -06:00
|
|
|
test14()
|
2013-09-18 12:44:57 -06:00
|
|
|
if !interp {
|
|
|
|
test14reflect1()
|
|
|
|
test14reflect2()
|
|
|
|
test15()
|
2014-10-22 09:06:15 -06:00
|
|
|
test16()
|
2013-09-18 12:44:57 -06:00
|
|
|
}
|
2010-03-31 12:47:09 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
func die() {
|
2010-04-01 23:31:27 -06:00
|
|
|
runtime.Breakpoint() // can't depend on panic
|
2010-03-31 12:47:09 -06:00
|
|
|
}
|
|
|
|
|
runtime, cmd/gc, cmd/ld: ignore method wrappers in recover
Bug #1:
Issue 5406 identified an interesting case:
defer iface.M()
may end up calling a wrapper that copies an indirect receiver
from the iface value and then calls the real M method. That's
two calls down, not just one, and so recover() == nil always
in the real M method, even during a panic.
[For the purposes of this entire discussion, a wrapper's
implementation is a function containing an ordinary call, not
the optimized tail call form that is somtimes possible. The
tail call does not create a second frame, so it is already
handled correctly.]
Fix this bug by introducing g->panicwrap, which counts the
number of bytes on current stack segment that are due to
wrapper calls that should not count against the recover
check. All wrapper functions must now adjust g->panicwrap up
on entry and back down on exit. This adds slightly to their
expense; on the x86 it is a single instruction at entry and
exit; on the ARM it is three. However, the alternative is to
make a call to recover depend on being able to walk the stack,
which I very much want to avoid. We have enough problems
walking the stack for garbage collection and profiling.
Also, if performance is critical in a specific case, it is already
faster to use a pointer receiver and avoid this kind of wrapper
entirely.
Bug #2:
The old code, which did not consider the possibility of two
calls, already contained a check to see if the call had split
its stack and so the panic-created segment was one behind the
current segment. In the wrapper case, both of the two calls
might split their stacks, so the panic-created segment can be
two behind the current segment.
Fix this by propagating the Stktop.panic flag forward during
stack splits instead of looking backward during recover.
Fixes #5406.
R=golang-dev, iant
CC=golang-dev
https://golang.org/cl/13367052
2013-09-12 12:00:16 -06:00
|
|
|
func mustRecoverBody(v1, v2, v3, x interface{}) {
|
|
|
|
v := v1
|
|
|
|
if v != nil {
|
|
|
|
println("spurious recover", v)
|
|
|
|
die()
|
|
|
|
}
|
|
|
|
v = v2
|
2010-03-31 12:47:09 -06:00
|
|
|
if v == nil {
|
2014-09-06 11:19:08 -06:00
|
|
|
println("missing recover", x.(int))
|
2010-04-01 23:31:27 -06:00
|
|
|
die() // panic is useless here
|
2010-03-31 12:47:09 -06:00
|
|
|
}
|
|
|
|
if v != x {
|
|
|
|
println("wrong value", v, x)
|
|
|
|
die()
|
|
|
|
}
|
2010-04-01 23:31:27 -06:00
|
|
|
|
2010-03-31 12:47:09 -06:00
|
|
|
// the value should be gone now regardless
|
runtime, cmd/gc, cmd/ld: ignore method wrappers in recover
Bug #1:
Issue 5406 identified an interesting case:
defer iface.M()
may end up calling a wrapper that copies an indirect receiver
from the iface value and then calls the real M method. That's
two calls down, not just one, and so recover() == nil always
in the real M method, even during a panic.
[For the purposes of this entire discussion, a wrapper's
implementation is a function containing an ordinary call, not
the optimized tail call form that is somtimes possible. The
tail call does not create a second frame, so it is already
handled correctly.]
Fix this bug by introducing g->panicwrap, which counts the
number of bytes on current stack segment that are due to
wrapper calls that should not count against the recover
check. All wrapper functions must now adjust g->panicwrap up
on entry and back down on exit. This adds slightly to their
expense; on the x86 it is a single instruction at entry and
exit; on the ARM it is three. However, the alternative is to
make a call to recover depend on being able to walk the stack,
which I very much want to avoid. We have enough problems
walking the stack for garbage collection and profiling.
Also, if performance is critical in a specific case, it is already
faster to use a pointer receiver and avoid this kind of wrapper
entirely.
Bug #2:
The old code, which did not consider the possibility of two
calls, already contained a check to see if the call had split
its stack and so the panic-created segment was one behind the
current segment. In the wrapper case, both of the two calls
might split their stacks, so the panic-created segment can be
two behind the current segment.
Fix this by propagating the Stktop.panic flag forward during
stack splits instead of looking backward during recover.
Fixes #5406.
R=golang-dev, iant
CC=golang-dev
https://golang.org/cl/13367052
2013-09-12 12:00:16 -06:00
|
|
|
v = v3
|
2010-03-31 12:47:09 -06:00
|
|
|
if v != nil {
|
|
|
|
println("recover didn't recover")
|
|
|
|
die()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
runtime, cmd/gc, cmd/ld: ignore method wrappers in recover
Bug #1:
Issue 5406 identified an interesting case:
defer iface.M()
may end up calling a wrapper that copies an indirect receiver
from the iface value and then calls the real M method. That's
two calls down, not just one, and so recover() == nil always
in the real M method, even during a panic.
[For the purposes of this entire discussion, a wrapper's
implementation is a function containing an ordinary call, not
the optimized tail call form that is somtimes possible. The
tail call does not create a second frame, so it is already
handled correctly.]
Fix this bug by introducing g->panicwrap, which counts the
number of bytes on current stack segment that are due to
wrapper calls that should not count against the recover
check. All wrapper functions must now adjust g->panicwrap up
on entry and back down on exit. This adds slightly to their
expense; on the x86 it is a single instruction at entry and
exit; on the ARM it is three. However, the alternative is to
make a call to recover depend on being able to walk the stack,
which I very much want to avoid. We have enough problems
walking the stack for garbage collection and profiling.
Also, if performance is critical in a specific case, it is already
faster to use a pointer receiver and avoid this kind of wrapper
entirely.
Bug #2:
The old code, which did not consider the possibility of two
calls, already contained a check to see if the call had split
its stack and so the panic-created segment was one behind the
current segment. In the wrapper case, both of the two calls
might split their stacks, so the panic-created segment can be
two behind the current segment.
Fix this by propagating the Stktop.panic flag forward during
stack splits instead of looking backward during recover.
Fixes #5406.
R=golang-dev, iant
CC=golang-dev
https://golang.org/cl/13367052
2013-09-12 12:00:16 -06:00
|
|
|
func doubleRecover() interface{} {
|
|
|
|
return recover()
|
|
|
|
}
|
|
|
|
|
|
|
|
func mustRecover(x interface{}) {
|
|
|
|
mustRecoverBody(doubleRecover(), recover(), recover(), x)
|
|
|
|
}
|
|
|
|
|
2010-03-31 12:47:09 -06:00
|
|
|
func mustNotRecover() {
|
|
|
|
v := recover()
|
|
|
|
if v != nil {
|
2010-04-01 23:31:27 -06:00
|
|
|
println("spurious recover", v)
|
2010-03-31 12:47:09 -06:00
|
|
|
die()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func withoutRecover() {
|
2010-04-01 23:31:27 -06:00
|
|
|
mustNotRecover() // because it's a sub-call
|
2010-03-31 12:47:09 -06:00
|
|
|
}
|
|
|
|
|
2014-10-22 09:06:15 -06:00
|
|
|
func withoutRecoverRecursive(n int) {
|
|
|
|
if n == 0 {
|
|
|
|
withoutRecoverRecursive(1)
|
|
|
|
} else {
|
|
|
|
v := recover()
|
|
|
|
if v != nil {
|
|
|
|
println("spurious recover (recursive)", v)
|
|
|
|
die()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-03-31 12:47:09 -06:00
|
|
|
func test1() {
|
2014-10-22 09:06:15 -06:00
|
|
|
defer mustNotRecover() // because mustRecover will squelch it
|
|
|
|
defer mustRecover(1) // because of panic below
|
|
|
|
defer withoutRecover() // should be no-op, leaving for mustRecover to find
|
|
|
|
defer withoutRecoverRecursive(0) // ditto
|
2010-03-31 12:47:09 -06:00
|
|
|
panic(1)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Repeat test1 with closures instead of standard function.
|
|
|
|
// Interesting because recover bases its decision
|
|
|
|
// on the frame pointer of its caller, and a closure's
|
|
|
|
// frame pointer is in the middle of its actual arguments
|
|
|
|
// (after the hidden ones for the closed-over variables).
|
|
|
|
func test1WithClosures() {
|
|
|
|
defer func() {
|
|
|
|
v := recover()
|
|
|
|
if v != nil {
|
|
|
|
println("spurious recover in closure")
|
|
|
|
die()
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
defer func(x interface{}) {
|
|
|
|
mustNotRecover()
|
|
|
|
v := recover()
|
|
|
|
if v == nil {
|
2014-09-06 11:19:08 -06:00
|
|
|
println("missing recover", x.(int))
|
2010-03-31 12:47:09 -06:00
|
|
|
die()
|
|
|
|
}
|
|
|
|
if v != x {
|
|
|
|
println("wrong value", v, x)
|
|
|
|
die()
|
|
|
|
}
|
|
|
|
}(1)
|
|
|
|
defer func() {
|
|
|
|
mustNotRecover()
|
|
|
|
}()
|
|
|
|
panic(1)
|
|
|
|
}
|
|
|
|
|
|
|
|
func test2() {
|
|
|
|
// Recover only sees the panic argument
|
|
|
|
// if it is called from a deferred call.
|
|
|
|
// It does not see the panic when called from a call within a deferred call (too late)
|
|
|
|
// nor does it see the panic when it *is* the deferred call (too early).
|
|
|
|
defer mustRecover(2)
|
2010-04-01 23:31:27 -06:00
|
|
|
defer recover() // should be no-op
|
2010-03-31 12:47:09 -06:00
|
|
|
panic(2)
|
|
|
|
}
|
|
|
|
|
|
|
|
func test3() {
|
|
|
|
defer mustNotRecover()
|
|
|
|
defer func() {
|
2010-04-01 23:31:27 -06:00
|
|
|
recover() // should squelch
|
2010-03-31 12:47:09 -06:00
|
|
|
}()
|
|
|
|
panic(3)
|
|
|
|
}
|
|
|
|
|
|
|
|
func test4() {
|
|
|
|
// Equivalent to test3 but using defer to make the call.
|
|
|
|
defer mustNotRecover()
|
|
|
|
defer func() {
|
2010-04-01 23:31:27 -06:00
|
|
|
defer recover() // should squelch
|
2010-03-31 12:47:09 -06:00
|
|
|
}()
|
|
|
|
panic(4)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check that closures can set output arguments.
|
|
|
|
// Run g(). If it panics, return x; else return deflt.
|
|
|
|
func try(g func(), deflt interface{}) (x interface{}) {
|
|
|
|
defer func() {
|
|
|
|
if v := recover(); v != nil {
|
|
|
|
x = v
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
defer g()
|
|
|
|
return deflt
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check that closures can set output arguments.
|
|
|
|
// Run g(). If it panics, return x; else return deflt.
|
|
|
|
func try1(g func(), deflt interface{}) (x interface{}) {
|
|
|
|
defer func() {
|
|
|
|
if v := recover(); v != nil {
|
|
|
|
x = v
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
defer g()
|
|
|
|
x = deflt
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func test5() {
|
|
|
|
v := try(func() { panic(5) }, 55).(int)
|
|
|
|
if v != 5 {
|
|
|
|
println("wrong value", v, 5)
|
|
|
|
die()
|
|
|
|
}
|
2010-04-01 23:31:27 -06:00
|
|
|
|
|
|
|
s := try(func() {}, "hi").(string)
|
2010-03-31 12:47:09 -06:00
|
|
|
if s != "hi" {
|
|
|
|
println("wrong value", s, "hi")
|
|
|
|
die()
|
|
|
|
}
|
|
|
|
|
|
|
|
v = try1(func() { panic(5) }, 55).(int)
|
|
|
|
if v != 5 {
|
|
|
|
println("try1 wrong value", v, 5)
|
|
|
|
die()
|
|
|
|
}
|
2010-04-01 23:31:27 -06:00
|
|
|
|
|
|
|
s = try1(func() {}, "hi").(string)
|
2010-03-31 12:47:09 -06:00
|
|
|
if s != "hi" {
|
|
|
|
println("try1 wrong value", s, "hi")
|
|
|
|
die()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// When a deferred big call starts, it must first
|
|
|
|
// create yet another stack segment to hold the
|
|
|
|
// giant frame for x. Make sure that doesn't
|
|
|
|
// confuse recover.
|
|
|
|
func big(mustRecover bool) {
|
|
|
|
var x [100000]int
|
|
|
|
x[0] = 1
|
|
|
|
x[99999] = 1
|
|
|
|
_ = x
|
2010-04-01 23:31:27 -06:00
|
|
|
|
2010-03-31 12:47:09 -06:00
|
|
|
v := recover()
|
|
|
|
if mustRecover {
|
|
|
|
if v == nil {
|
|
|
|
println("missing big recover")
|
|
|
|
die()
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if v != nil {
|
|
|
|
println("spurious big recover")
|
|
|
|
die()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func test6() {
|
|
|
|
defer big(false)
|
|
|
|
defer big(true)
|
|
|
|
panic(6)
|
|
|
|
}
|
|
|
|
|
|
|
|
func test6WithClosures() {
|
|
|
|
defer func() {
|
|
|
|
var x [100000]int
|
|
|
|
x[0] = 1
|
|
|
|
x[99999] = 1
|
|
|
|
_ = x
|
|
|
|
if recover() != nil {
|
|
|
|
println("spurious big closure recover")
|
|
|
|
die()
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
defer func() {
|
|
|
|
var x [100000]int
|
|
|
|
x[0] = 1
|
|
|
|
x[99999] = 1
|
|
|
|
_ = x
|
|
|
|
if recover() == nil {
|
|
|
|
println("missing big closure recover")
|
|
|
|
die()
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
panic("6WithClosures")
|
|
|
|
}
|
|
|
|
|
|
|
|
func test7() {
|
|
|
|
ok := false
|
|
|
|
func() {
|
|
|
|
// should panic, then call mustRecover 7, which stops the panic.
|
|
|
|
// then should keep processing ordinary defers earlier than that one
|
|
|
|
// before returning.
|
|
|
|
// this test checks that the defer func on the next line actually runs.
|
|
|
|
defer func() { ok = true }()
|
|
|
|
defer mustRecover(7)
|
|
|
|
panic(7)
|
|
|
|
}()
|
|
|
|
if !ok {
|
|
|
|
println("did not run ok func")
|
|
|
|
die()
|
|
|
|
}
|
|
|
|
}
|
2012-03-01 09:24:03 -07:00
|
|
|
|
|
|
|
func varargs(s *int, a ...int) {
|
|
|
|
*s = 0
|
|
|
|
for _, v := range a {
|
|
|
|
*s += v
|
|
|
|
}
|
|
|
|
if recover() != nil {
|
|
|
|
*s += 100
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func test8a() (r int) {
|
|
|
|
defer varargs(&r, 1, 2, 3)
|
|
|
|
panic(0)
|
|
|
|
}
|
|
|
|
|
|
|
|
func test8b() (r int) {
|
|
|
|
defer varargs(&r, 4, 5, 6)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func test8() {
|
|
|
|
if test8a() != 106 || test8b() != 15 {
|
|
|
|
println("wrong value")
|
|
|
|
die()
|
|
|
|
}
|
|
|
|
}
|
runtime, cmd/gc, cmd/ld: ignore method wrappers in recover
Bug #1:
Issue 5406 identified an interesting case:
defer iface.M()
may end up calling a wrapper that copies an indirect receiver
from the iface value and then calls the real M method. That's
two calls down, not just one, and so recover() == nil always
in the real M method, even during a panic.
[For the purposes of this entire discussion, a wrapper's
implementation is a function containing an ordinary call, not
the optimized tail call form that is somtimes possible. The
tail call does not create a second frame, so it is already
handled correctly.]
Fix this bug by introducing g->panicwrap, which counts the
number of bytes on current stack segment that are due to
wrapper calls that should not count against the recover
check. All wrapper functions must now adjust g->panicwrap up
on entry and back down on exit. This adds slightly to their
expense; on the x86 it is a single instruction at entry and
exit; on the ARM it is three. However, the alternative is to
make a call to recover depend on being able to walk the stack,
which I very much want to avoid. We have enough problems
walking the stack for garbage collection and profiling.
Also, if performance is critical in a specific case, it is already
faster to use a pointer receiver and avoid this kind of wrapper
entirely.
Bug #2:
The old code, which did not consider the possibility of two
calls, already contained a check to see if the call had split
its stack and so the panic-created segment was one behind the
current segment. In the wrapper case, both of the two calls
might split their stacks, so the panic-created segment can be
two behind the current segment.
Fix this by propagating the Stktop.panic flag forward during
stack splits instead of looking backward during recover.
Fixes #5406.
R=golang-dev, iant
CC=golang-dev
https://golang.org/cl/13367052
2013-09-12 12:00:16 -06:00
|
|
|
|
2013-09-18 12:44:57 -06:00
|
|
|
type I interface {
|
|
|
|
M()
|
|
|
|
}
|
runtime, cmd/gc, cmd/ld: ignore method wrappers in recover
Bug #1:
Issue 5406 identified an interesting case:
defer iface.M()
may end up calling a wrapper that copies an indirect receiver
from the iface value and then calls the real M method. That's
two calls down, not just one, and so recover() == nil always
in the real M method, even during a panic.
[For the purposes of this entire discussion, a wrapper's
implementation is a function containing an ordinary call, not
the optimized tail call form that is somtimes possible. The
tail call does not create a second frame, so it is already
handled correctly.]
Fix this bug by introducing g->panicwrap, which counts the
number of bytes on current stack segment that are due to
wrapper calls that should not count against the recover
check. All wrapper functions must now adjust g->panicwrap up
on entry and back down on exit. This adds slightly to their
expense; on the x86 it is a single instruction at entry and
exit; on the ARM it is three. However, the alternative is to
make a call to recover depend on being able to walk the stack,
which I very much want to avoid. We have enough problems
walking the stack for garbage collection and profiling.
Also, if performance is critical in a specific case, it is already
faster to use a pointer receiver and avoid this kind of wrapper
entirely.
Bug #2:
The old code, which did not consider the possibility of two
calls, already contained a check to see if the call had split
its stack and so the panic-created segment was one behind the
current segment. In the wrapper case, both of the two calls
might split their stacks, so the panic-created segment can be
two behind the current segment.
Fix this by propagating the Stktop.panic flag forward during
stack splits instead of looking backward during recover.
Fixes #5406.
R=golang-dev, iant
CC=golang-dev
https://golang.org/cl/13367052
2013-09-12 12:00:16 -06:00
|
|
|
|
|
|
|
// pointer receiver, so no wrapper in i.M()
|
2013-09-18 12:44:57 -06:00
|
|
|
type T1 struct{}
|
runtime, cmd/gc, cmd/ld: ignore method wrappers in recover
Bug #1:
Issue 5406 identified an interesting case:
defer iface.M()
may end up calling a wrapper that copies an indirect receiver
from the iface value and then calls the real M method. That's
two calls down, not just one, and so recover() == nil always
in the real M method, even during a panic.
[For the purposes of this entire discussion, a wrapper's
implementation is a function containing an ordinary call, not
the optimized tail call form that is somtimes possible. The
tail call does not create a second frame, so it is already
handled correctly.]
Fix this bug by introducing g->panicwrap, which counts the
number of bytes on current stack segment that are due to
wrapper calls that should not count against the recover
check. All wrapper functions must now adjust g->panicwrap up
on entry and back down on exit. This adds slightly to their
expense; on the x86 it is a single instruction at entry and
exit; on the ARM it is three. However, the alternative is to
make a call to recover depend on being able to walk the stack,
which I very much want to avoid. We have enough problems
walking the stack for garbage collection and profiling.
Also, if performance is critical in a specific case, it is already
faster to use a pointer receiver and avoid this kind of wrapper
entirely.
Bug #2:
The old code, which did not consider the possibility of two
calls, already contained a check to see if the call had split
its stack and so the panic-created segment was one behind the
current segment. In the wrapper case, both of the two calls
might split their stacks, so the panic-created segment can be
two behind the current segment.
Fix this by propagating the Stktop.panic flag forward during
stack splits instead of looking backward during recover.
Fixes #5406.
R=golang-dev, iant
CC=golang-dev
https://golang.org/cl/13367052
2013-09-12 12:00:16 -06:00
|
|
|
|
|
|
|
func (*T1) M() {
|
|
|
|
mustRecoverBody(doubleRecover(), recover(), recover(), 9)
|
|
|
|
}
|
|
|
|
|
|
|
|
func test9() {
|
|
|
|
var i I = &T1{}
|
|
|
|
defer i.M()
|
|
|
|
panic(9)
|
|
|
|
}
|
|
|
|
|
|
|
|
func test9reflect1() {
|
|
|
|
f := reflect.ValueOf(&T1{}).Method(0).Interface().(func())
|
|
|
|
defer f()
|
|
|
|
panic(9)
|
|
|
|
}
|
|
|
|
|
|
|
|
func test9reflect2() {
|
|
|
|
f := reflect.TypeOf(&T1{}).Method(0).Func.Interface().(func(*T1))
|
|
|
|
defer f(&T1{})
|
|
|
|
panic(9)
|
|
|
|
}
|
|
|
|
|
|
|
|
// word-sized value receiver, so no wrapper in i.M()
|
|
|
|
type T2 uintptr
|
|
|
|
|
|
|
|
func (T2) M() {
|
|
|
|
mustRecoverBody(doubleRecover(), recover(), recover(), 10)
|
|
|
|
}
|
|
|
|
|
|
|
|
func test10() {
|
|
|
|
var i I = T2(0)
|
|
|
|
defer i.M()
|
|
|
|
panic(10)
|
|
|
|
}
|
|
|
|
|
|
|
|
func test10reflect1() {
|
|
|
|
f := reflect.ValueOf(T2(0)).Method(0).Interface().(func())
|
|
|
|
defer f()
|
|
|
|
panic(10)
|
|
|
|
}
|
|
|
|
|
|
|
|
func test10reflect2() {
|
|
|
|
f := reflect.TypeOf(T2(0)).Method(0).Func.Interface().(func(T2))
|
|
|
|
defer f(T2(0))
|
|
|
|
panic(10)
|
|
|
|
}
|
|
|
|
|
|
|
|
// tiny receiver, so basic wrapper in i.M()
|
2013-09-18 12:44:57 -06:00
|
|
|
type T3 struct{}
|
runtime, cmd/gc, cmd/ld: ignore method wrappers in recover
Bug #1:
Issue 5406 identified an interesting case:
defer iface.M()
may end up calling a wrapper that copies an indirect receiver
from the iface value and then calls the real M method. That's
two calls down, not just one, and so recover() == nil always
in the real M method, even during a panic.
[For the purposes of this entire discussion, a wrapper's
implementation is a function containing an ordinary call, not
the optimized tail call form that is somtimes possible. The
tail call does not create a second frame, so it is already
handled correctly.]
Fix this bug by introducing g->panicwrap, which counts the
number of bytes on current stack segment that are due to
wrapper calls that should not count against the recover
check. All wrapper functions must now adjust g->panicwrap up
on entry and back down on exit. This adds slightly to their
expense; on the x86 it is a single instruction at entry and
exit; on the ARM it is three. However, the alternative is to
make a call to recover depend on being able to walk the stack,
which I very much want to avoid. We have enough problems
walking the stack for garbage collection and profiling.
Also, if performance is critical in a specific case, it is already
faster to use a pointer receiver and avoid this kind of wrapper
entirely.
Bug #2:
The old code, which did not consider the possibility of two
calls, already contained a check to see if the call had split
its stack and so the panic-created segment was one behind the
current segment. In the wrapper case, both of the two calls
might split their stacks, so the panic-created segment can be
two behind the current segment.
Fix this by propagating the Stktop.panic flag forward during
stack splits instead of looking backward during recover.
Fixes #5406.
R=golang-dev, iant
CC=golang-dev
https://golang.org/cl/13367052
2013-09-12 12:00:16 -06:00
|
|
|
|
|
|
|
func (T3) M() {
|
|
|
|
mustRecoverBody(doubleRecover(), recover(), recover(), 11)
|
|
|
|
}
|
|
|
|
|
|
|
|
func test11() {
|
|
|
|
var i I = T3{}
|
|
|
|
defer i.M()
|
|
|
|
panic(11)
|
|
|
|
}
|
|
|
|
|
|
|
|
func test11reflect1() {
|
|
|
|
f := reflect.ValueOf(T3{}).Method(0).Interface().(func())
|
|
|
|
defer f()
|
|
|
|
panic(11)
|
|
|
|
}
|
|
|
|
|
|
|
|
func test11reflect2() {
|
|
|
|
f := reflect.TypeOf(T3{}).Method(0).Func.Interface().(func(T3))
|
|
|
|
defer f(T3{})
|
|
|
|
panic(11)
|
|
|
|
}
|
|
|
|
|
2014-09-06 11:19:08 -06:00
|
|
|
// tiny receiver, so basic wrapper in i.M()
|
|
|
|
type T3deeper struct{}
|
|
|
|
|
|
|
|
func (T3deeper) M() {
|
|
|
|
badstate() // difference from T3
|
|
|
|
mustRecoverBody(doubleRecover(), recover(), recover(), 111)
|
|
|
|
}
|
|
|
|
|
|
|
|
func test111() {
|
|
|
|
var i I = T3deeper{}
|
|
|
|
defer i.M()
|
|
|
|
panic(111)
|
|
|
|
}
|
|
|
|
|
|
|
|
type Tiny struct{}
|
|
|
|
|
|
|
|
func (Tiny) M() {
|
|
|
|
panic(112)
|
|
|
|
}
|
|
|
|
|
|
|
|
// i.M is a wrapper, and i.M panics.
|
|
|
|
//
|
|
|
|
// This is a torture test for an old implementation of recover that
|
|
|
|
// tried to deal with wrapper functions by doing some argument
|
|
|
|
// positioning math on both entry and exit. Doing anything on exit
|
|
|
|
// is a problem because sometimes functions exit via panic instead
|
|
|
|
// of an ordinary return, so panic would have to know to do the
|
|
|
|
// same math when unwinding the stack. It gets complicated fast.
|
|
|
|
// This particular test never worked with the old scheme, because
|
|
|
|
// panic never did the right unwinding math.
|
|
|
|
//
|
|
|
|
// The new scheme adjusts Panic.argp on entry to a wrapper.
|
|
|
|
// It has no exit work, so if a wrapper is interrupted by a panic,
|
|
|
|
// there's no cleanup that panic itself must do.
|
|
|
|
// This test just works now.
|
|
|
|
func badstate() {
|
|
|
|
defer func() {
|
|
|
|
recover()
|
|
|
|
}()
|
|
|
|
var i I = Tiny{}
|
|
|
|
i.M()
|
|
|
|
}
|
|
|
|
|
runtime, cmd/gc, cmd/ld: ignore method wrappers in recover
Bug #1:
Issue 5406 identified an interesting case:
defer iface.M()
may end up calling a wrapper that copies an indirect receiver
from the iface value and then calls the real M method. That's
two calls down, not just one, and so recover() == nil always
in the real M method, even during a panic.
[For the purposes of this entire discussion, a wrapper's
implementation is a function containing an ordinary call, not
the optimized tail call form that is somtimes possible. The
tail call does not create a second frame, so it is already
handled correctly.]
Fix this bug by introducing g->panicwrap, which counts the
number of bytes on current stack segment that are due to
wrapper calls that should not count against the recover
check. All wrapper functions must now adjust g->panicwrap up
on entry and back down on exit. This adds slightly to their
expense; on the x86 it is a single instruction at entry and
exit; on the ARM it is three. However, the alternative is to
make a call to recover depend on being able to walk the stack,
which I very much want to avoid. We have enough problems
walking the stack for garbage collection and profiling.
Also, if performance is critical in a specific case, it is already
faster to use a pointer receiver and avoid this kind of wrapper
entirely.
Bug #2:
The old code, which did not consider the possibility of two
calls, already contained a check to see if the call had split
its stack and so the panic-created segment was one behind the
current segment. In the wrapper case, both of the two calls
might split their stacks, so the panic-created segment can be
two behind the current segment.
Fix this by propagating the Stktop.panic flag forward during
stack splits instead of looking backward during recover.
Fixes #5406.
R=golang-dev, iant
CC=golang-dev
https://golang.org/cl/13367052
2013-09-12 12:00:16 -06:00
|
|
|
// large receiver, so basic wrapper in i.M()
|
|
|
|
type T4 [2]string
|
|
|
|
|
|
|
|
func (T4) M() {
|
|
|
|
mustRecoverBody(doubleRecover(), recover(), recover(), 12)
|
|
|
|
}
|
|
|
|
|
|
|
|
func test12() {
|
|
|
|
var i I = T4{}
|
|
|
|
defer i.M()
|
|
|
|
panic(12)
|
|
|
|
}
|
|
|
|
|
|
|
|
func test12reflect1() {
|
|
|
|
f := reflect.ValueOf(T4{}).Method(0).Interface().(func())
|
|
|
|
defer f()
|
|
|
|
panic(12)
|
|
|
|
}
|
|
|
|
|
|
|
|
func test12reflect2() {
|
|
|
|
f := reflect.TypeOf(T4{}).Method(0).Func.Interface().(func(T4))
|
|
|
|
defer f(T4{})
|
|
|
|
panic(12)
|
|
|
|
}
|
|
|
|
|
|
|
|
// enormous receiver, so wrapper splits stack to call M
|
|
|
|
type T5 [8192]byte
|
|
|
|
|
|
|
|
func (T5) M() {
|
|
|
|
mustRecoverBody(doubleRecover(), recover(), recover(), 13)
|
|
|
|
}
|
|
|
|
|
|
|
|
func test13() {
|
|
|
|
var i I = T5{}
|
|
|
|
defer i.M()
|
|
|
|
panic(13)
|
|
|
|
}
|
|
|
|
|
|
|
|
func test13reflect1() {
|
|
|
|
f := reflect.ValueOf(T5{}).Method(0).Interface().(func())
|
|
|
|
defer f()
|
|
|
|
panic(13)
|
|
|
|
}
|
|
|
|
|
|
|
|
func test13reflect2() {
|
|
|
|
f := reflect.TypeOf(T5{}).Method(0).Func.Interface().(func(T5))
|
|
|
|
defer f(T5{})
|
|
|
|
panic(13)
|
|
|
|
}
|
|
|
|
|
|
|
|
// enormous receiver + enormous method frame, so wrapper splits stack to call M,
|
|
|
|
// and then M splits stack to allocate its frame.
|
|
|
|
// recover must look back two frames to find the panic.
|
|
|
|
type T6 [8192]byte
|
|
|
|
|
|
|
|
var global byte
|
|
|
|
|
|
|
|
func (T6) M() {
|
|
|
|
var x [8192]byte
|
|
|
|
x[0] = 1
|
|
|
|
x[1] = 2
|
|
|
|
for i := range x {
|
|
|
|
global += x[i]
|
|
|
|
}
|
|
|
|
mustRecoverBody(doubleRecover(), recover(), recover(), 14)
|
|
|
|
}
|
|
|
|
|
|
|
|
func test14() {
|
|
|
|
var i I = T6{}
|
|
|
|
defer i.M()
|
|
|
|
panic(14)
|
|
|
|
}
|
|
|
|
|
|
|
|
func test14reflect1() {
|
|
|
|
f := reflect.ValueOf(T6{}).Method(0).Interface().(func())
|
|
|
|
defer f()
|
|
|
|
panic(14)
|
|
|
|
}
|
|
|
|
|
|
|
|
func test14reflect2() {
|
|
|
|
f := reflect.TypeOf(T6{}).Method(0).Func.Interface().(func(T6))
|
|
|
|
defer f(T6{})
|
|
|
|
panic(14)
|
|
|
|
}
|
|
|
|
|
|
|
|
// function created by reflect.MakeFunc
|
|
|
|
|
|
|
|
func reflectFunc(args []reflect.Value) (results []reflect.Value) {
|
|
|
|
mustRecoverBody(doubleRecover(), recover(), recover(), 15)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func test15() {
|
|
|
|
f := reflect.MakeFunc(reflect.TypeOf((func())(nil)), reflectFunc).Interface().(func())
|
|
|
|
defer f()
|
|
|
|
panic(15)
|
|
|
|
}
|
2014-10-22 09:06:15 -06:00
|
|
|
|
|
|
|
func reflectFunc2(args []reflect.Value) (results []reflect.Value) {
|
|
|
|
// This will call reflectFunc3
|
|
|
|
args[0].Interface().(func())()
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func reflectFunc3(args []reflect.Value) (results []reflect.Value) {
|
|
|
|
if v := recover(); v != nil {
|
|
|
|
println("spurious recover", v)
|
|
|
|
die()
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func test16() {
|
|
|
|
defer mustRecover(16)
|
|
|
|
|
|
|
|
f2 := reflect.MakeFunc(reflect.TypeOf((func(func()))(nil)), reflectFunc2).Interface().(func(func()))
|
|
|
|
f3 := reflect.MakeFunc(reflect.TypeOf((func())(nil)), reflectFunc3).Interface().(func())
|
|
|
|
defer f2(f3)
|
|
|
|
|
|
|
|
panic(16)
|
|
|
|
}
|