Skip to content

Commit

Permalink
ctx.SetPanic: support handle runtimeError/plainError
Browse files Browse the repository at this point in the history
  • Loading branch information
visualfc committed Feb 13, 2025
1 parent 325fc6d commit 71e5557
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 74 deletions.
57 changes: 29 additions & 28 deletions builtin.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import (

// callBuiltin interprets a call to builtin fn with arguments args,
// returning its result.
func (inter *Interp) callBuiltin(caller *frame, fn *ssa.Builtin, args []value, ssaArgs []ssa.Value) value {
func (inter *Interp) callBuiltin(fr *frame, fn *ssa.Builtin, args []value, ssaArgs []ssa.Value) value {
switch fnName := fn.Name(); fnName {
case "append":
if len(args) == 1 {
Expand All @@ -47,7 +47,7 @@ func (inter *Interp) callBuiltin(caller *frame, fn *ssa.Builtin, args []value, s
i0 := v0.Len()
i1 := v1.Len()
if i0+i1 < i0 {
panic(runtimeError(errAppendOutOfRange))
panic(fr.runtimeError(fn, errAppendOutOfRange))
}
return reflect.AppendSlice(v0, v1).Interface()

Expand Down Expand Up @@ -127,14 +127,14 @@ func (inter *Interp) callBuiltin(caller *frame, fn *ssa.Builtin, args []value, s
case "panic":
// ssa.Panic handles most cases; this is only for "go
// panic" or "defer panic".
var err error = PanicError{stack: debugStack(caller), Value: args[0]}
var err error = PanicError{stack: debugStack(fr), Value: args[0]}
if inter.ctx.panicFunc != nil {
err = inter.ctx.handlePanic(caller, fn, err)
err = inter.ctx.handlePanic(fr, fn, err)
}
panic(err)

case "recover":
return doRecover(caller)
return doRecover(fr)

case "ssa:wrapnilchk":
recv := args[0]
Expand All @@ -147,7 +147,7 @@ func (inter *Interp) callBuiltin(caller *frame, fn *ssa.Builtin, args []value, s
} else {
info = recvType
}
panic(plainError(fmt.Sprintf("value method %s.%s called using nil *%s pointer",
panic(fr.plainError(fn, fmt.Sprintf("value method %s.%s called using nil *%s pointer",
recvType, methodName, info)))
}
return recv
Expand All @@ -166,11 +166,11 @@ func (inter *Interp) callBuiltin(caller *frame, fn *ssa.Builtin, args []value, s
if length == 0 {
return reflect.New(reflect.SliceOf(etyp)).Elem().Interface()
}
panic(runtimeError("unsafe.Slice: ptr is nil and len is not zero"))
panic(fr.runtimeError(fn, "unsafe.Slice: ptr is nil and len is not zero"))
}
mem, overflow := mulUintptr(etyp.Size(), uintptr(length))
if overflow || mem > -uintptr(ptr.Pointer()) {
panic(runtimeError("unsafe.Slice: len out of range"))
panic(fr.runtimeError(fn, "unsafe.Slice: len out of range"))
}
typ := reflect.ArrayOf(length, etyp)
v := reflect.NewAt(typ, unsafe.Pointer(ptr.Pointer()))
Expand Down Expand Up @@ -210,11 +210,11 @@ func (inter *Interp) callBuiltin(caller *frame, fn *ssa.Builtin, args []value, s
if length == 0 {
return ""
}
panic(runtimeError("unsafe.String: ptr is nil and len is not zero"))
panic(fr.runtimeError(fn, "unsafe.String: ptr is nil and len is not zero"))
}
mem, overflow := mulUintptr(1, uintptr(length))
if overflow || mem > -uintptr(unsafe.Pointer(ptr)) {
panic(runtimeError("unsafe.String: len out of range"))
panic(fr.runtimeError(fn, "unsafe.String: len out of range"))
}
sh := reflect.StringHeader{Data: uintptr(unsafe.Pointer(ptr)), Len: length}
return *(*string)(unsafe.Pointer(&sh))
Expand All @@ -235,7 +235,7 @@ func (inter *Interp) callBuiltin(caller *frame, fn *ssa.Builtin, args []value, s

// callBuiltinDiscardsResult interprets a call to builtin fn with arguments args,
// discards its result.
func (inter *Interp) callBuiltinDiscardsResult(caller *frame, fn *ssa.Builtin, args []value, ssaArgs []ssa.Value) {
func (inter *Interp) callBuiltinDiscardsResult(fr *frame, fn *ssa.Builtin, args []value, ssaArgs []ssa.Value) {
switch fnName := fn.Name(); fnName {
case "append":
panic("discards result of " + fnName)
Expand Down Expand Up @@ -288,14 +288,14 @@ func (inter *Interp) callBuiltinDiscardsResult(caller *frame, fn *ssa.Builtin, a
case "panic":
// ssa.Panic handles most cases; this is only for "go
// panic" or "defer panic".
var err error = PanicError{stack: debugStack(caller), Value: args[0]}
var err error = PanicError{stack: debugStack(fr), Value: args[0]}
if inter.ctx.panicFunc != nil {
err = inter.ctx.handlePanic(caller, fn, err)
err = inter.ctx.handlePanic(fr, fn, err)
}
panic(err)

case "recover":
doRecover(caller)
doRecover(fr)

case "ssa:wrapnilchk":
recv := args[0]
Expand All @@ -308,7 +308,7 @@ func (inter *Interp) callBuiltinDiscardsResult(caller *frame, fn *ssa.Builtin, a
} else {
info = recvType
}
panic(plainError(fmt.Sprintf("value method %s.%s called using nil *%s pointer",
panic(fr.plainError(fn, fmt.Sprintf("value method %s.%s called using nil *%s pointer",
recvType, methodName, info)))
}

Expand Down Expand Up @@ -356,7 +356,7 @@ func (interp *Interp) makeBuiltinByStack(fn *ssa.Builtin, ssaArgs []ssa.Value, i
i0 := v0.Len()
i1 := v1.Len()
if i0+i1 < i0 {
panic(runtimeError(errAppendOutOfRange))
panic(fr.runtimeError(fn, errAppendOutOfRange))
}
fr.setReg(ir, reflect.AppendSlice(v0, v1).Interface())
}
Expand Down Expand Up @@ -492,7 +492,7 @@ func (interp *Interp) makeBuiltinByStack(fn *ssa.Builtin, ssaArgs []ssa.Value, i
} else {
info = recvType
}
panic(plainError(fmt.Sprintf("value method %s.%s called using nil *%s pointer",
panic(fr.plainError(fn, fmt.Sprintf("value method %s.%s called using nil *%s pointer",
recvType, methodName, info)))
}
fr.setReg(ir, recv)
Expand Down Expand Up @@ -520,11 +520,11 @@ func (interp *Interp) makeBuiltinByStack(fn *ssa.Builtin, ssaArgs []ssa.Value, i
fr.setReg(ir, reflect.New(reflect.SliceOf(etyp)).Elem().Interface())
return
}
panic(runtimeError("unsafe.Slice: ptr is nil and len is not zero"))
panic(fr.runtimeError(fn, "unsafe.Slice: ptr is nil and len is not zero"))
}
mem, overflow := mulUintptr(etyp.Size(), uintptr(length))
if overflow || mem > -uintptr(ptr.Pointer()) {
panic(runtimeError("unsafe.Slice: len out of range"))
panic(fr.runtimeError(fn, "unsafe.Slice: len out of range"))
}
typ := reflect.ArrayOf(length, etyp)
v := reflect.NewAt(typ, unsafe.Pointer(ptr.Pointer()))
Expand Down Expand Up @@ -570,11 +570,11 @@ func (interp *Interp) makeBuiltinByStack(fn *ssa.Builtin, ssaArgs []ssa.Value, i
fr.setReg(ir, "")
return
}
panic(runtimeError("unsafe.String: ptr is nil and len is not zero"))
panic(fr.runtimeError(fn, "unsafe.String: ptr is nil and len is not zero"))
}
mem, overflow := mulUintptr(1, uintptr(length))
if overflow || mem > -uintptr(unsafe.Pointer(ptr)) {
panic(runtimeError("unsafe.String: len out of range"))
panic(fr.runtimeError(fn, "unsafe.String: len out of range"))
}
sh := reflect.StringHeader{Data: uintptr(unsafe.Pointer(ptr)), Len: length}
fr.setReg(ir, *(*string)(unsafe.Pointer(&sh)))
Expand Down Expand Up @@ -603,7 +603,7 @@ func (interp *Interp) makeBuiltinByStack(fn *ssa.Builtin, ssaArgs []ssa.Value, i
}
case "Offsetof": // instance of generic function
return func(fr *frame) {
offset, err := builtinOffsetof(fr.pfn, fr.ipc-1)
offset, err := builtinOffsetof(fr, fn, fr.ipc-1)
if err != nil {
panic(err)
}
Expand Down Expand Up @@ -693,27 +693,28 @@ func mulUintptr(a, b uintptr) (uintptr, bool) {
return a * b, overflow
}

func builtinOffsetof(pfn *function, pc int) (int64, error) {
func builtinOffsetof(fr *frame, fn *ssa.Builtin, pc int) (int64, error) {
pfn := fr.pfn
pos := pfn.ssaInstrs[pc].Pos()
paths, info, ok := pathEnclosingInterval(pfn.Interp.ctx, pos)
if !ok {
return -1, plainError("unsafe.Offsetof not found code")
return -1, fr.plainError(fn, "unsafe.Offsetof not found code")
}
call, ok := paths[0].(*ast.CallExpr)
if !ok {
return -1, plainError("unsafe.Offsetof not found call")
return -1, fr.plainError(fn, "unsafe.Offsetof not found call")
}
selx, ok := call.Args[0].(*ast.SelectorExpr)
if !ok {
return -1, plainError("unsafe.Offsetof not found selector expr")
return -1, fr.plainError(fn, "unsafe.Offsetof not found selector expr")
}
sel, _ := info.Selections[selx]
if sel == nil {
return -1, plainError("unsafe.Offsetof not found selector type")
return -1, fr.plainError(fn, "unsafe.Offsetof not found selector type")
}
instrs, found := foundFieldAddr(pfn, pc)
if !found || len(sel.Index()) > len(instrs) {
return -1, plainError("unsafe.Offsetof not found FieldAddr instr")
return -1, fr.plainError(fn, "unsafe.Offsetof not found FieldAddr instr")
}
instr := instrs[len(sel.Index())-1]
return selOffsetof(pfn.Interp.ctx.sizes, instr.X.Type().Underlying().(*types.Pointer).Elem().Underlying().(*types.Struct), sel.Index(), selx.Sel.Name)
Expand Down
35 changes: 29 additions & 6 deletions interp.go
Original file line number Diff line number Diff line change
Expand Up @@ -480,6 +480,20 @@ func (fr *frame) copyReg(dst register, src register) {
fr.stack[dst] = fr.stack[src]
}

func (fr *frame) runtimeError(instr funcInstr, text string) error {
if fr.interp.ctx.panicFunc != nil {
return fr.interp.ctx.handlePanic(fr, instr, runtimeError(text))
}
return runtimeError(text)
}

func (fr *frame) plainError(instr funcInstr, text string) error {
if fr.interp.ctx.panicFunc != nil {
return fr.interp.ctx.handlePanic(fr, instr, plainError(text))
}
return plainError(text)
}

type _panic struct {
arg interface{}
link *_panic
Expand Down Expand Up @@ -1263,6 +1277,13 @@ func (i *Interp) RunFunc(name string, args ...Value) (r Value, err error) {
if i.ctx.Mode&DisableRecover != 0 {
return
}
wrapPanicError := func(p interface{}) error {
pfr := fr
for pfr.callee != nil {
pfr = pfr.callee
}
return PanicError{stack: debugStack(pfr), Value: p}
}
switch p := recover().(type) {
case nil:
// nothing
Expand All @@ -1277,16 +1298,18 @@ func (i *Interp) RunFunc(name string, args ...Value) (r Value, err error) {
i.exitCode = <-i.chexit
atomic.StoreInt32(&i.exited, 1)
}
case runtime.Error:
err = p
case PanicError:
err = p
case runtime.Error:
err = p
if i.ctx.panicFunc != nil {
i.ctx.handlePanic(fr, i.mainpkg.Func(name), err)
}
default:
pfr := fr
for pfr.callee != nil {
pfr = pfr.callee
err = wrapPanicError(p)
if i.ctx.panicFunc != nil {
i.ctx.handlePanic(fr, i.mainpkg.Func(name), err)
}
err = PanicError{stack: debugStack(pfr), Value: p}
}
}()
if fn := i.mainpkg.Func(name); fn != nil {
Expand Down
28 changes: 14 additions & 14 deletions opblock.go
Original file line number Diff line number Diff line change
Expand Up @@ -520,7 +520,7 @@ func makeInstr(interp *Interp, pfn *function, instr ssa.Instruction) func(fr *fr
size := fr.reg(is)
buffer := asInt(size)
if buffer < 0 {
panic(runtimeError("makechan: size out of range"))
panic(fr.runtimeError(instr, "makechan: size out of range"))
}
fr.setReg(ir, reflect.MakeChan(typ, buffer).Interface())
}
Expand All @@ -530,7 +530,7 @@ func makeInstr(interp *Interp, pfn *function, instr ssa.Instruction) func(fr *fr
size := fr.reg(is)
buffer := asInt(size)
if buffer < 0 {
panic(runtimeError("makechan: size out of range"))
panic(fr.runtimeError(instr, "makechan: size out of range"))
}
fr.setReg(ir, reflect.MakeChan(ctyp, buffer).Convert(typ).Interface())
}
Expand All @@ -556,11 +556,11 @@ func makeInstr(interp *Interp, pfn *function, instr ssa.Instruction) func(fr *fr
return func(fr *frame) {
Len := asInt(fr.reg(il))
if Len < 0 || Len >= maxMemLen {
panic(runtimeError("makeslice: len out of range"))
panic(fr.runtimeError(instr, "makeslice: len out of range"))
}
Cap := asInt(fr.reg(ic))
if Cap < 0 || Cap >= maxMemLen {
panic(runtimeError("makeslice: cap out of range"))
panic(fr.runtimeError(instr, "makeslice: cap out of range"))
}
fr.setReg(ir, reflect.MakeSlice(typ, Len, Cap).Interface())
}
Expand All @@ -587,7 +587,7 @@ func makeInstr(interp *Interp, pfn *function, instr ssa.Instruction) func(fr *fr
return func(fr *frame) {
v, err := fieldAddrX(fr.reg(ix), instr.Field)
if err != nil {
panic(runtimeError(err.Error()))
panic(fr.runtimeError(instr, err.Error()))
}
fr.setReg(ir, v)
}
Expand All @@ -597,7 +597,7 @@ func makeInstr(interp *Interp, pfn *function, instr ssa.Instruction) func(fr *fr
return func(fr *frame) {
v, err := fieldX(fr.reg(ix), instr.Field)
if err != nil {
panic(runtimeError(err.Error()))
panic(fr.runtimeError(instr, err.Error()))
}
fr.setReg(ir, v)
}
Expand All @@ -616,15 +616,15 @@ func makeInstr(interp *Interp, pfn *function, instr ssa.Instruction) func(fr *fr
case reflect.Slice:
case reflect.Array:
case reflect.Invalid:
panic(runtimeError("invalid memory address or nil pointer dereference"))
panic(fr.runtimeError(instr, "invalid memory address or nil pointer dereference"))
default:
panic(fmt.Sprintf("unexpected x type in IndexAddr: %T", x))
}
index := asInt(idx)
if index < 0 {
panic(runtimeError(fmt.Sprintf("index out of range [%v]", index)))
panic(fr.runtimeError(instr, fmt.Sprintf("index out of range [%v]", index)))
} else if length := v.Len(); index >= length {
panic(runtimeError(fmt.Sprintf("index out of range [%v] with length %v", index, length)))
panic(fr.runtimeError(instr, fmt.Sprintf("index out of range [%v] with length %v", index, length)))
}
fr.setReg(ir, v.Index(index).Addr().Interface())
}
Expand All @@ -638,9 +638,9 @@ func makeInstr(interp *Interp, pfn *function, instr ssa.Instruction) func(fr *fr
index := asInt(idx)
v := reflect.ValueOf(x)
if index < 0 {
panic(runtimeError(fmt.Sprintf("index out of range [%v]", index)))
panic(fr.runtimeError(instr, fmt.Sprintf("index out of range [%v]", index)))
} else if length := v.Len(); index >= length {
panic(runtimeError(fmt.Sprintf("index out of range [%v] with length %v", index, length)))
panic(fr.runtimeError(instr, fmt.Sprintf("index out of range [%v] with length %v", index, length)))
}
fr.setReg(ir, v.Index(index).Interface())
}
Expand Down Expand Up @@ -748,7 +748,7 @@ func makeInstr(interp *Interp, pfn *function, instr ssa.Instruction) func(fr *fr
vLen := v.Len()
tLen := typ.Elem().Len()
if tLen > vLen {
panic(runtimeError(fmt.Sprintf(errSliceToArrayPointer, vLen, tLen)))
panic(fr.runtimeError(instr, fmt.Sprintf(errSliceToArrayPointer, vLen, tLen)))
}
fr.setReg(ir, v.Convert(typ).Interface())
}
Expand Down Expand Up @@ -788,12 +788,12 @@ func makeInstr(interp *Interp, pfn *function, instr ssa.Instruction) func(fr *fr
ix, kx, vx := pfn.regIndex3(instr.X)
if kx.isStatic() {
return func(fr *frame) {
fr.setReg(ir, typeAssert(interp, instr, typ, xtyp, vx))
fr.setReg(ir, typeAssert(fr, instr, typ, xtyp, vx))
}
}
return func(fr *frame) {
v := fr.reg(ix)
fr.setReg(ir, typeAssert(interp, instr, typ, xtyp, v))
fr.setReg(ir, typeAssert(fr, instr, typ, xtyp, v))
}
case *ssa.Extract:
if *instr.Referrers() == nil {
Expand Down
Loading

0 comments on commit 71e5557

Please sign in to comment.