mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-09-14 16:32:59 +00:00
runtime: avoid heap allocs for plain errors
This commit is contained in:
+4
-4
@@ -207,7 +207,7 @@ func (ch *channel) trySend(value unsafe.Pointer) (sent bool, wake *task.Task) {
|
||||
// Note: we cannot currently recover from this panic.
|
||||
// There's some state in the select statement especially that would be
|
||||
// corrupted if we allowed recovering from this panic.
|
||||
runtimePanic("send on closed channel")
|
||||
runtimePanic(errSendOnClosedChannel)
|
||||
}
|
||||
|
||||
// There is no value in the buffer and we have a receiver available. Copy
|
||||
@@ -266,7 +266,7 @@ func chanSend(ch *channel, value unsafe.Pointer, op *channelOp) {
|
||||
// closed while sending).
|
||||
if t.DataUint32() == chanOperationClosed {
|
||||
// Oops, this channel was closed while sending!
|
||||
runtimePanic("send on closed channel")
|
||||
runtimePanic(errSendOnClosedChannel)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -349,7 +349,7 @@ func chanRecv(ch *channel, value unsafe.Pointer, op *channelOp) bool {
|
||||
func chanClose(ch *channel) {
|
||||
if ch == nil {
|
||||
// Not allowed by the language spec.
|
||||
runtimePanic("close of nil channel")
|
||||
runtimePanic(errCloseNilChannel)
|
||||
}
|
||||
|
||||
mask := interrupt.Disable()
|
||||
@@ -359,7 +359,7 @@ func chanClose(ch *channel) {
|
||||
// Not allowed by the language spec.
|
||||
ch.lock.Unlock()
|
||||
interrupt.Restore(mask)
|
||||
runtimePanic("close of closed channel")
|
||||
runtimePanic(errCloseClosedChannel)
|
||||
}
|
||||
|
||||
// Collect waiters and wake them after unlocking.
|
||||
|
||||
@@ -12,3 +12,30 @@ type plainError string
|
||||
|
||||
func (e plainError) Error() string { return string(e) }
|
||||
func (e plainError) RuntimeError() {}
|
||||
|
||||
const (
|
||||
errNilPointer = plainError("nil pointer dereference")
|
||||
errNilMap = plainError("assignment to entry in nil map")
|
||||
errIndexOutOfRange = plainError("index out of range")
|
||||
errSliceOutOfRange = plainError("slice out of range")
|
||||
errSliceToArray = plainError("slice smaller than array")
|
||||
errUnsafeSliceLength = plainError("unsafe.Slice/String: len out of range")
|
||||
errChannelTooBig = plainError("new channel is too big")
|
||||
errNegativeShift = plainError("negative shift")
|
||||
errDivideByZero = plainError("divide by zero")
|
||||
errBlockingExported = plainError("trying to do blocking operation in exported function")
|
||||
errTimersUnsupported = plainError("timers not supported without a scheduler")
|
||||
errIntegerOverflow = plainError("integer overflow")
|
||||
errUnsupportedSignal = plainError("unsupported signal number")
|
||||
errUnsupportedExit = plainError("unsupported: syscall.Exit")
|
||||
errSendOnClosedChannel = plainError("send on closed channel")
|
||||
errCloseNilChannel = plainError("close of nil channel")
|
||||
errCloseClosedChannel = plainError("close of closed channel")
|
||||
errWasmBeforeInit = plainError("//go:wasmexport function called before runtime initialization")
|
||||
errWasmAfterMain = plainError("//go:wasmexport function called after main.main returned")
|
||||
errWasmDidNotFinish = plainError("//go:wasmexport function did not finish")
|
||||
errUncomparable = plainError("comparing un-comparable type")
|
||||
errTypeAssert = plainError("type assert failed")
|
||||
errSchedulerDisabled = plainError("scheduler is disabled")
|
||||
errOutOfMemory = plainError("out of memory")
|
||||
)
|
||||
|
||||
@@ -49,7 +49,7 @@ func alloc(size uintptr, layout unsafe.Pointer) unsafe.Pointer {
|
||||
heapptr += size
|
||||
if heapptr < addr {
|
||||
// The allocation size overflowed the heap pointer.
|
||||
runtimePanic("out of memory")
|
||||
runtimePanic(errOutOfMemory)
|
||||
}
|
||||
for heapptr > heapEnd {
|
||||
// Try to increase the heap and check again.
|
||||
|
||||
@@ -832,7 +832,7 @@ func hashmapInterfaceHash(itf interface{}, seed uintptr) uint32 {
|
||||
}
|
||||
return hash
|
||||
default:
|
||||
runtimePanic("comparing un-comparable type")
|
||||
runtimePanic(errUncomparable)
|
||||
return 0 // unreachable
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@ func reflectValueEqual(x, y reflectlite.Value) bool {
|
||||
case reflectlite.Interface:
|
||||
return reflectValueEqual(x.Elem(), y.Elem())
|
||||
default:
|
||||
runtimePanic("comparing un-comparable type")
|
||||
runtimePanic(errUncomparable)
|
||||
return false // unreachable
|
||||
}
|
||||
}
|
||||
@@ -86,7 +86,7 @@ func reflectValueEqual(x, y reflectlite.Value) bool {
|
||||
// returns false.
|
||||
func interfaceTypeAssert(ok bool) {
|
||||
if !ok {
|
||||
runtimePanic("type assert failed")
|
||||
runtimePanic(errTypeAssert)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+21
-17
@@ -84,11 +84,11 @@ func panicOrGoexit(message interface{}, panicking panicState) {
|
||||
abort()
|
||||
}
|
||||
|
||||
// Cause a runtime panic, which is (currently) always a string.
|
||||
func runtimePanic(msg string) {
|
||||
// As long as this function is inined, llvm.returnaddress(0) will return
|
||||
// Cause a recoverable runtime panic.
|
||||
func runtimePanic(err Error) {
|
||||
// As long as this function is inlined, llvm.returnaddress(0) will return
|
||||
// something sensible.
|
||||
runtimePanicAt(returnAddress(0), msg)
|
||||
runtimePanicAt(returnAddress(0), err)
|
||||
}
|
||||
|
||||
// runtimeFatal terminates for runtime failures that cannot safely be
|
||||
@@ -103,7 +103,7 @@ func runtimeFatal(msg string) {
|
||||
abort()
|
||||
}
|
||||
|
||||
func runtimePanicAt(addr unsafe.Pointer, msg string) {
|
||||
func runtimePanicAt(addr unsafe.Pointer, err Error) {
|
||||
if panicStrategy() == tinygo.PanicStrategyTrap {
|
||||
trap()
|
||||
}
|
||||
@@ -112,7 +112,7 @@ func runtimePanicAt(addr unsafe.Pointer, msg string) {
|
||||
if frame != nil {
|
||||
// Use the normal panic mechanism so that this runtime error
|
||||
// can be recovered with recover().
|
||||
frame.PanicValue = plainError(msg)
|
||||
frame.PanicValue = err
|
||||
frame.Panicking = panicTrue | (frame.Panicking & panicGoexit)
|
||||
tinygo_longjmp(frame)
|
||||
// unreachable
|
||||
@@ -128,11 +128,15 @@ func runtimePanicAt(addr unsafe.Pointer, msg string) {
|
||||
} else {
|
||||
printstring("panic: runtime error: ")
|
||||
}
|
||||
printstring(msg)
|
||||
printstring(err.Error())
|
||||
printnl()
|
||||
abort()
|
||||
}
|
||||
|
||||
func runtimePanicSchedulerDisabled() {
|
||||
runtimePanicAt(returnAddress(0), errSchedulerDisabled)
|
||||
}
|
||||
|
||||
// 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
|
||||
@@ -220,54 +224,54 @@ func _recover(useParentFrame bool) interface{} {
|
||||
|
||||
// Panic when trying to dereference a nil pointer.
|
||||
func nilPanic() {
|
||||
runtimePanicAt(returnAddress(0), "nil pointer dereference")
|
||||
runtimePanicAt(returnAddress(0), errNilPointer)
|
||||
}
|
||||
|
||||
// Panic when trying to add an entry to a nil map
|
||||
func nilMapPanic() {
|
||||
runtimePanicAt(returnAddress(0), "assignment to entry in nil map")
|
||||
runtimePanicAt(returnAddress(0), errNilMap)
|
||||
}
|
||||
|
||||
// Panic when trying to access an array or slice out of bounds.
|
||||
func lookupPanic() {
|
||||
runtimePanicAt(returnAddress(0), "index out of range")
|
||||
runtimePanicAt(returnAddress(0), errIndexOutOfRange)
|
||||
}
|
||||
|
||||
// Panic when trying to slice a slice out of bounds.
|
||||
func slicePanic() {
|
||||
runtimePanicAt(returnAddress(0), "slice out of range")
|
||||
runtimePanicAt(returnAddress(0), errSliceOutOfRange)
|
||||
}
|
||||
|
||||
// Panic when trying to convert a slice to an array pointer (Go 1.17+) and the
|
||||
// slice is shorter than the array.
|
||||
func sliceToArrayPointerPanic() {
|
||||
runtimePanicAt(returnAddress(0), "slice smaller than array")
|
||||
runtimePanicAt(returnAddress(0), errSliceToArray)
|
||||
}
|
||||
|
||||
// Panic when calling unsafe.Slice() (Go 1.17+) or unsafe.String() (Go 1.20+)
|
||||
// with a len that's too large (which includes if the ptr is nil and len is
|
||||
// nonzero).
|
||||
func unsafeSlicePanic() {
|
||||
runtimePanicAt(returnAddress(0), "unsafe.Slice/String: len out of range")
|
||||
runtimePanicAt(returnAddress(0), errUnsafeSliceLength)
|
||||
}
|
||||
|
||||
// Panic when trying to create a new channel that is too big.
|
||||
func chanMakePanic() {
|
||||
runtimePanicAt(returnAddress(0), "new channel is too big")
|
||||
runtimePanicAt(returnAddress(0), errChannelTooBig)
|
||||
}
|
||||
|
||||
// Panic when a shift value is negative.
|
||||
func negativeShiftPanic() {
|
||||
runtimePanicAt(returnAddress(0), "negative shift")
|
||||
runtimePanicAt(returnAddress(0), errNegativeShift)
|
||||
}
|
||||
|
||||
// Panic when there is a divide by zero.
|
||||
func divideByZeroPanic() {
|
||||
runtimePanicAt(returnAddress(0), "divide by zero")
|
||||
runtimePanicAt(returnAddress(0), errDivideByZero)
|
||||
}
|
||||
|
||||
func blockingPanic() {
|
||||
runtimePanicAt(returnAddress(0), "trying to do blocking operation in exported function")
|
||||
runtimePanicAt(returnAddress(0), errBlockingExported)
|
||||
}
|
||||
|
||||
//go:linkname fips_fatal crypto/internal/fips140.fatal
|
||||
|
||||
@@ -34,7 +34,7 @@ func syscall_Exit(code int) {
|
||||
// Because this is the "unknown" target we can't call an exit function.
|
||||
// But we also can't just return since the program will likely expect this
|
||||
// function to never return. So we panic instead.
|
||||
runtimePanic("unsupported: syscall.Exit")
|
||||
runtimePanic(errUnsupportedExit)
|
||||
}
|
||||
|
||||
// There is not yet any support for any form of parallelism on WebAssembly, so these
|
||||
|
||||
@@ -151,9 +151,9 @@ func tinygo_sigpanic() {
|
||||
sig := tinygo_caught_signal
|
||||
switch sig {
|
||||
case sig_SIGSEGV, sig_SIGBUS:
|
||||
runtimePanic("nil pointer dereference")
|
||||
runtimePanic(errNilPointer)
|
||||
case sig_SIGFPE:
|
||||
runtimePanic("divide by zero")
|
||||
runtimePanic(errDivideByZero)
|
||||
default:
|
||||
runtimeFatal("signal")
|
||||
}
|
||||
@@ -382,7 +382,7 @@ func signal_enable(s uint32) {
|
||||
if s >= 32 {
|
||||
// TODO: to support higher signal numbers, we need to turn
|
||||
// receivedSignals into a uint32 array.
|
||||
runtimePanicAt(returnAddress(0), "unsupported signal number")
|
||||
runtimePanicAt(returnAddress(0), errUnsupportedSignal)
|
||||
}
|
||||
|
||||
// This is intentonally a non-atomic store. This is safe, since hasSignals
|
||||
@@ -399,7 +399,7 @@ func signal_ignore(s uint32) {
|
||||
if s >= 32 {
|
||||
// TODO: to support higher signal numbers, we need to turn
|
||||
// receivedSignals into a uint32 array.
|
||||
runtimePanicAt(returnAddress(0), "unsupported signal number")
|
||||
runtimePanicAt(returnAddress(0), errUnsupportedSignal)
|
||||
}
|
||||
tinygo_signal_ignore(s)
|
||||
}
|
||||
@@ -409,7 +409,7 @@ func signal_disable(s uint32) {
|
||||
if s >= 32 {
|
||||
// TODO: to support higher signal numbers, we need to turn
|
||||
// receivedSignals into a uint32 array.
|
||||
runtimePanicAt(returnAddress(0), "unsupported signal number")
|
||||
runtimePanicAt(returnAddress(0), errUnsupportedSignal)
|
||||
}
|
||||
tinygo_signal_disable(s)
|
||||
}
|
||||
|
||||
@@ -66,9 +66,9 @@ var initializeCalled bool
|
||||
func wasmExportCheckRun() {
|
||||
switch {
|
||||
case !initializeCalled:
|
||||
runtimePanic("//go:wasmexport function called before runtime initialization")
|
||||
runtimePanic(errWasmBeforeInit)
|
||||
case mainExited:
|
||||
runtimePanic("//go:wasmexport function called after main.main returned")
|
||||
runtimePanic(errWasmAfterMain)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,6 +81,6 @@ func wasmExportCheckRun() {
|
||||
func wasmExportRun(done *bool) {
|
||||
scheduler(true)
|
||||
if !*done {
|
||||
runtimePanic("//go:wasmexport function did not finish")
|
||||
runtimePanic(errWasmDidNotFinish)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -315,11 +315,11 @@ func tinygo_init_exception_handler()
|
||||
func tinygo_sigpanic_windows(exceptionCode int32) {
|
||||
switch uint32(exceptionCode) {
|
||||
case _EXCEPTION_ACCESS_VIOLATION, _EXCEPTION_IN_PAGE_ERROR:
|
||||
runtimePanic("nil pointer dereference")
|
||||
runtimePanic(errNilPointer)
|
||||
case _EXCEPTION_INT_DIVIDE_BY_ZERO:
|
||||
runtimePanic("divide by zero")
|
||||
runtimePanic(errDivideByZero)
|
||||
case _EXCEPTION_INT_OVERFLOW:
|
||||
runtimePanic("integer overflow")
|
||||
runtimePanic(errIntegerOverflow)
|
||||
default:
|
||||
runtimeFatal("unknown exception")
|
||||
}
|
||||
|
||||
@@ -60,15 +60,15 @@ func NumCPU() int {
|
||||
}
|
||||
|
||||
func addTimer(tim *timerNode) {
|
||||
runtimePanic("timers not supported without a scheduler")
|
||||
runtimePanic(errTimersUnsupported)
|
||||
}
|
||||
|
||||
func reAddTimer(tn *timerNode) {
|
||||
runtimePanic("timers not supported without a scheduler")
|
||||
runtimePanic(errTimersUnsupported)
|
||||
}
|
||||
|
||||
func removeTimer(tim *timer) *timerNode {
|
||||
runtimePanic("timers not supported without a scheduler")
|
||||
runtimePanic(errTimersUnsupported)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user