mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-15 00:13:43 +00:00
compiler, runtime: make slice lookup panics recoverable
This commit is contained in:
@@ -383,6 +383,7 @@ TEST_PACKAGES_LINUX := \
|
|||||||
crypto/hmac \
|
crypto/hmac \
|
||||||
debug/dwarf \
|
debug/dwarf \
|
||||||
debug/plan9obj \
|
debug/plan9obj \
|
||||||
|
encoding/binary \
|
||||||
go/constant \
|
go/constant \
|
||||||
image \
|
image \
|
||||||
io/ioutil \
|
io/ioutil \
|
||||||
@@ -406,6 +407,7 @@ TEST_PACKAGES_WINDOWS := \
|
|||||||
compress/flate \
|
compress/flate \
|
||||||
crypto/des \
|
crypto/des \
|
||||||
crypto/hmac \
|
crypto/hmac \
|
||||||
|
encoding/binary \
|
||||||
go/constant \
|
go/constant \
|
||||||
math/bits \
|
math/bits \
|
||||||
strconv \
|
strconv \
|
||||||
|
|||||||
+4
-10
@@ -31,7 +31,7 @@ func (b *builder) createLookupBoundsCheck(arrayLen, index llvm.Value) {
|
|||||||
|
|
||||||
// Now do the bounds check: index >= arrayLen
|
// Now do the bounds check: index >= arrayLen
|
||||||
outOfBounds := b.CreateICmp(llvm.IntUGE, index, arrayLen, "")
|
outOfBounds := b.CreateICmp(llvm.IntUGE, index, arrayLen, "")
|
||||||
b.createRuntimeAssert(outOfBounds, "lookup", "lookupPanic", false)
|
b.createRuntimeAssert(outOfBounds, "lookup", "lookupPanic", true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// createSliceBoundsCheck emits a bounds check before a slicing operation to make
|
// createSliceBoundsCheck emits a bounds check before a slicing operation to make
|
||||||
@@ -230,7 +230,7 @@ func (b *builder) createDivideByZeroCheck(y llvm.Value) {
|
|||||||
|
|
||||||
// createRuntimeAssert is a common function to create a new branch on an assert
|
// createRuntimeAssert is a common function to create a new branch on an assert
|
||||||
// bool, calling an assert func if the assert value is true (1).
|
// bool, calling an assert func if the assert value is true (1).
|
||||||
func (b *builder) createRuntimeAssert(assert llvm.Value, blockPrefix, assertFunc string, invoke bool) {
|
func (b *builder) createRuntimeAssert(assert llvm.Value, blockPrefix, assertFunc string, isInvoke bool) {
|
||||||
// Check whether we can resolve this check at compile time.
|
// Check whether we can resolve this check at compile time.
|
||||||
if !assert.IsAConstantInt().IsNil() {
|
if !assert.IsAConstantInt().IsNil() {
|
||||||
val := assert.ZExtValue()
|
val := assert.ZExtValue()
|
||||||
@@ -245,23 +245,17 @@ func (b *builder) createRuntimeAssert(assert llvm.Value, blockPrefix, assertFunc
|
|||||||
// current insert position.
|
// current insert position.
|
||||||
faultBlock := b.ctx.AddBasicBlock(b.llvmFn, blockPrefix+".throw")
|
faultBlock := b.ctx.AddBasicBlock(b.llvmFn, blockPrefix+".throw")
|
||||||
nextBlock := b.insertBasicBlock(blockPrefix + ".next")
|
nextBlock := b.insertBasicBlock(blockPrefix + ".next")
|
||||||
b.blockExits[b.currentBlock] = nextBlock // adjust outgoing block for phi nodes
|
|
||||||
|
|
||||||
// Now branch to the out-of-bounds or the regular block.
|
// Now branch to the out-of-bounds or the regular block.
|
||||||
b.CreateCondBr(assert, faultBlock, nextBlock)
|
b.CreateCondBr(assert, faultBlock, nextBlock)
|
||||||
|
|
||||||
// Fail: the assert triggered so panic.
|
// Fail: the assert triggered so panic.
|
||||||
b.SetInsertPointAtEnd(faultBlock)
|
b.SetInsertPointAtEnd(faultBlock)
|
||||||
if invoke {
|
b.createRuntimeCallCommon(assertFunc, nil, "", isInvoke)
|
||||||
// This runtime panic is recoverable.
|
|
||||||
b.createRuntimeInvoke(assertFunc, nil, "")
|
|
||||||
} else {
|
|
||||||
// This runtime panic is not recoverable.
|
|
||||||
b.createRuntimeCall(assertFunc, nil, "")
|
|
||||||
}
|
|
||||||
b.CreateUnreachable()
|
b.CreateUnreachable()
|
||||||
|
|
||||||
// Ok: assert didn't trigger so continue normally.
|
// Ok: assert didn't trigger so continue normally.
|
||||||
|
b.blockExits[b.currentBlock] = nextBlock // adjust outgoing block for phi nodes
|
||||||
b.SetInsertPointAtEnd(nextBlock)
|
b.SetInsertPointAtEnd(nextBlock)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,5 +21,6 @@ func (r runtimeError) RuntimeError() {}
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
divideError error = runtimeError{"runtime error: integer divide by zero"}
|
divideError error = runtimeError{"runtime error: integer divide by zero"}
|
||||||
|
lookupError error = runtimeError{"runtime error: index out of range"}
|
||||||
overflowError error = runtimeError{"runtime error: integer overflow"}
|
overflowError error = runtimeError{"runtime error: integer overflow"}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -187,7 +187,7 @@ func nilMapPanic() {
|
|||||||
|
|
||||||
// Panic when trying to access an array or slice out of bounds.
|
// Panic when trying to access an array or slice out of bounds.
|
||||||
func lookupPanic() {
|
func lookupPanic() {
|
||||||
runtimePanicAt(returnAddress(0), "index out of range")
|
_panic(lookupError)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Panic when trying to slice a slice out of bounds.
|
// Panic when trying to slice a slice out of bounds.
|
||||||
|
|||||||
Vendored
+11
@@ -32,6 +32,7 @@ func main() {
|
|||||||
|
|
||||||
println("\n# runtime panics")
|
println("\n# runtime panics")
|
||||||
runtimePanicDivByZero(1, 0)
|
runtimePanicDivByZero(1, 0)
|
||||||
|
runtimePanicLookup([]int{1, 2, 3}, 10)
|
||||||
|
|
||||||
println("\n# runtime.Goexit")
|
println("\n# runtime.Goexit")
|
||||||
runtimeGoexit()
|
runtimeGoexit()
|
||||||
@@ -127,6 +128,16 @@ func runtimePanicDivByZero(a, b int) int {
|
|||||||
return a / b
|
return a / b
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func runtimePanicLookup(slice []int, index int) int {
|
||||||
|
defer func() {
|
||||||
|
if err := recover(); err != nil {
|
||||||
|
println("recovered:", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return slice[index]
|
||||||
|
}
|
||||||
|
|
||||||
func runtimeGoexit() {
|
func runtimeGoexit() {
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
|
|||||||
Reference in New Issue
Block a user