mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-21 12:59:04 +00:00
compiler: make map and channel panics recoverable
Change hashmap set operations and channel send/close from createRuntimeCall to createRuntimeInvoke. This places a setjmp checkpoint before each call, allowing runtimePanicAt to safely longjmp when these operations panic.
This commit is contained in:
+2
-2
@@ -48,7 +48,7 @@ func (b *builder) createChanSend(instr *ssa.Send) {
|
|||||||
channelOpAlloca, channelOpAllocaSize := b.createTemporaryAlloca(channelOp, "chan.op")
|
channelOpAlloca, channelOpAllocaSize := b.createTemporaryAlloca(channelOp, "chan.op")
|
||||||
|
|
||||||
// Do the send.
|
// Do the send.
|
||||||
b.createRuntimeCall("chanSend", []llvm.Value{ch, valueAlloca, channelOpAlloca}, "")
|
b.createRuntimeInvoke("chanSend", []llvm.Value{ch, valueAlloca, channelOpAlloca}, "")
|
||||||
|
|
||||||
// End the lifetime of the allocas.
|
// End the lifetime of the allocas.
|
||||||
// This also works around a bug in CoroSplit, at least in LLVM 8:
|
// This also works around a bug in CoroSplit, at least in LLVM 8:
|
||||||
@@ -101,7 +101,7 @@ func (b *builder) createChanRecv(unop *ssa.UnOp) llvm.Value {
|
|||||||
|
|
||||||
// createChanClose closes the given channel.
|
// createChanClose closes the given channel.
|
||||||
func (b *builder) createChanClose(ch llvm.Value) {
|
func (b *builder) createChanClose(ch llvm.Value) {
|
||||||
b.createRuntimeCall("chanClose", []llvm.Value{ch}, "")
|
b.createRuntimeInvoke("chanClose", []llvm.Value{ch}, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
// createSelect emits all IR necessary for a select statements. That's a
|
// createSelect emits all IR necessary for a select statements. That's a
|
||||||
|
|||||||
+2
-2
@@ -133,7 +133,7 @@ func (b *builder) createMapUpdate(keyType types.Type, m, key, value llvm.Value,
|
|||||||
if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 {
|
if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 {
|
||||||
// key is a string
|
// key is a string
|
||||||
params := []llvm.Value{m, key, valueAlloca}
|
params := []llvm.Value{m, key, valueAlloca}
|
||||||
b.createRuntimeCall("hashmapStringSet", params, "")
|
b.createRuntimeInvoke("hashmapStringSet", params, "")
|
||||||
} else {
|
} else {
|
||||||
// Key stored at actual type.
|
// Key stored at actual type.
|
||||||
keyAlloca, keySize := b.createTemporaryAlloca(key.Type(), "hashmap.key")
|
keyAlloca, keySize := b.createTemporaryAlloca(key.Type(), "hashmap.key")
|
||||||
@@ -143,7 +143,7 @@ func (b *builder) createMapUpdate(keyType types.Type, m, key, value llvm.Value,
|
|||||||
fnName = "hashmapGenericSet"
|
fnName = "hashmapGenericSet"
|
||||||
}
|
}
|
||||||
params := []llvm.Value{m, keyAlloca, valueAlloca}
|
params := []llvm.Value{m, keyAlloca, valueAlloca}
|
||||||
b.createRuntimeCall(fnName, params, "")
|
b.createRuntimeInvoke(fnName, params, "")
|
||||||
b.emitLifetimeEnd(keyAlloca, keySize)
|
b.emitLifetimeEnd(keyAlloca, keySize)
|
||||||
}
|
}
|
||||||
b.emitLifetimeEnd(valueAlloca, valueSize)
|
b.emitLifetimeEnd(valueAlloca, valueSize)
|
||||||
|
|||||||
Vendored
+20
@@ -41,6 +41,9 @@ func main() {
|
|||||||
|
|
||||||
println("\n# recover runtime errors")
|
println("\n# recover runtime errors")
|
||||||
recoverRuntimeError()
|
recoverRuntimeError()
|
||||||
|
|
||||||
|
println("\n# recover from nil map and closed channel")
|
||||||
|
recoverNilMapAndChan()
|
||||||
}
|
}
|
||||||
|
|
||||||
func recoverSimple() {
|
func recoverSimple() {
|
||||||
@@ -241,3 +244,20 @@ func recoverMustPanic(name string, f func()) {
|
|||||||
}()
|
}()
|
||||||
f()
|
f()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test recovering from nil map assignment and closed channel send.
|
||||||
|
func recoverNilMapAndChan() {
|
||||||
|
recoverMustPanic("nil map", func() {
|
||||||
|
var m map[string]int
|
||||||
|
m["x"] = 1
|
||||||
|
})
|
||||||
|
recoverMustPanic("closed chan", func() {
|
||||||
|
ch := make(chan int)
|
||||||
|
close(ch)
|
||||||
|
ch <- 1
|
||||||
|
})
|
||||||
|
recoverMustPanic("close nil chan", func() {
|
||||||
|
var ch chan int
|
||||||
|
close(ch)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
Vendored
+5
@@ -44,3 +44,8 @@ outer recovered: repanic value
|
|||||||
recovered: slice
|
recovered: slice
|
||||||
recovered: type assert
|
recovered: type assert
|
||||||
recovered: empty interface type assert
|
recovered: empty interface type assert
|
||||||
|
|
||||||
|
# recover from nil map and closed channel
|
||||||
|
recovered: nil map
|
||||||
|
recovered: closed chan
|
||||||
|
recovered: close nil chan
|
||||||
|
|||||||
Reference in New Issue
Block a user