mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-10 05:53:39 +00:00
compiler: implement recover() built-in function
This commit is contained in:
committed by
Ron Evans
parent
79ba6a50c3
commit
8d6b210c09
+76
-4
@@ -1,12 +1,35 @@
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"internal/task"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// trap is a compiler hint that this function cannot be executed. It is
|
||||
// translated into either a trap instruction or a call to abort().
|
||||
//export llvm.trap
|
||||
func trap()
|
||||
|
||||
// Inline assembly stub. It is essentially C longjmp but modified a bit for the
|
||||
// purposes of TinyGo. It restores the stack pointer and jumps to the given pc.
|
||||
//export tinygo_longjmp
|
||||
func tinygo_longjmp(sp, pc unsafe.Pointer)
|
||||
|
||||
// Compiler intrinsic.
|
||||
// Returns whether recover is supported on the current architecture.
|
||||
func supportsRecover() bool
|
||||
|
||||
// Builtin function panic(msg), used as a compiler intrinsic.
|
||||
func _panic(message interface{}) {
|
||||
if supportsRecover() {
|
||||
frame := task.Current().DeferFrame
|
||||
if frame != nil {
|
||||
frame.PanicValue = message
|
||||
frame.Panicking = true
|
||||
tinygo_longjmp(frame.JumpSP, frame.JumpPC)
|
||||
// unreachable
|
||||
}
|
||||
}
|
||||
printstring("panic: ")
|
||||
printitf(message)
|
||||
printnl()
|
||||
@@ -20,10 +43,59 @@ func runtimePanic(msg string) {
|
||||
abort()
|
||||
}
|
||||
|
||||
// Try to recover a panicking goroutine.
|
||||
func _recover() interface{} {
|
||||
// Deferred functions are currently not executed during panic, so there is
|
||||
// no way this can return anything besides nil.
|
||||
// Called at the start of a function that includes a deferred call.
|
||||
// It gets passed in the stack-allocated defer frame and configures it.
|
||||
// Note that the frame is not zeroed yet, so we need to initialize all values
|
||||
// that will be used.
|
||||
//go:inline
|
||||
//go:nobounds
|
||||
func setupDeferFrame(frame *task.DeferFrame, jumpSP unsafe.Pointer) {
|
||||
currentTask := task.Current()
|
||||
frame.Previous = currentTask.DeferFrame
|
||||
frame.JumpSP = jumpSP
|
||||
frame.Panicking = false
|
||||
currentTask.DeferFrame = frame
|
||||
}
|
||||
|
||||
// Called right before the return instruction. It pops the defer frame from the
|
||||
// linked list of defer frames. It also re-raises a panic if the goroutine is
|
||||
// still panicking.
|
||||
//go:inline
|
||||
//go:nobounds
|
||||
func destroyDeferFrame(frame *task.DeferFrame) {
|
||||
task.Current().DeferFrame = frame.Previous
|
||||
if frame.Panicking {
|
||||
// We're still panicking!
|
||||
// Re-raise the panic now.
|
||||
_panic(frame.PanicValue)
|
||||
}
|
||||
}
|
||||
|
||||
// _recover is the built-in recover() function. It tries to recover a currently
|
||||
// panicking goroutine.
|
||||
// useParentFrame is set when the caller of runtime._recover has a defer frame
|
||||
// itself. In that case, recover() shouldn't check that frame but one frame up.
|
||||
func _recover(useParentFrame bool) interface{} {
|
||||
if !supportsRecover() {
|
||||
// Compiling without stack unwinding support, so make this a no-op.
|
||||
return nil
|
||||
}
|
||||
// TODO: somehow check that recover() is called directly by a deferred
|
||||
// function in a panicking goroutine. Maybe this can be done by comparing
|
||||
// the frame pointer?
|
||||
frame := task.Current().DeferFrame
|
||||
if useParentFrame {
|
||||
// Don't recover panic from the current frame (which can't be panicking
|
||||
// already), but instead from the previous frame.
|
||||
frame = frame.Previous
|
||||
}
|
||||
if frame != nil && frame.Panicking {
|
||||
// Only the first call to recover returns the panic value. It also stops
|
||||
// the panicking sequence, hence setting panicking to false.
|
||||
frame.Panicking = false
|
||||
return frame.PanicValue
|
||||
}
|
||||
// Not panicking, so return a nil interface.
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user