Skip to content

Commit

Permalink
Context.SetPanic
Browse files Browse the repository at this point in the history
  • Loading branch information
visualfc committed Jan 9, 2025
1 parent 716a544 commit 49008cc
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 8 deletions.
26 changes: 19 additions & 7 deletions builtin.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,11 @@ 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".
panic(PanicError{stack: debugStack(caller), Value: args[0]})
var err error = PanicError{stack: debugStack(caller), Value: args[0]}
if inter.ctx.panicFunc != nil {
err = inter.ctx.handlePanic(caller, fn, err)
}
panic(err)

case "recover":
return doRecover(caller)
Expand Down Expand Up @@ -284,7 +288,11 @@ 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".
panic(PanicError{stack: debugStack(caller), Value: args[0]})
var err error = PanicError{stack: debugStack(caller), Value: args[0]}
if inter.ctx.panicFunc != nil {
err = inter.ctx.handlePanic(caller, fn, err)
}
panic(err)

case "recover":
doRecover(caller)
Expand Down Expand Up @@ -328,8 +336,8 @@ func (inter *Interp) callBuiltinDiscardsResult(caller *frame, fn *ssa.Builtin, a

// callBuiltin interprets a call to builtin fn with arguments args,
// returning its result.
func (interp *Interp) callBuiltinByStack(caller *frame, fn string, ssaArgs []ssa.Value, ir register, ia []register) {
switch fn {
func (interp *Interp) callBuiltinByStack(caller *frame, fname string, fn *ssa.Builtin, ssaArgs []ssa.Value, ir register, ia []register) {
switch fname {
case "append":
if len(ia) == 1 {
caller.copyReg(ir, ia[0])
Expand Down Expand Up @@ -365,7 +373,7 @@ func (interp *Interp) callBuiltinByStack(caller *frame, fn string, ssaArgs []ssa
reflect.ValueOf(arg0).SetMapIndex(reflect.ValueOf(arg1), reflect.Value{})

case "print", "println": // print(any, ...)
ln := fn == "println"
ln := fname == "println"
var buf bytes.Buffer
for i := 0; i < len(ia); i++ {
arg := caller.reg(ia[i])
Expand Down Expand Up @@ -436,7 +444,11 @@ func (interp *Interp) callBuiltinByStack(caller *frame, fn string, ssaArgs []ssa
// ssa.Panic handles most cases; this is only for "go
// panic" or "defer panic".
arg0 := caller.reg(ia[0])
panic(PanicError{stack: debugStack(caller), Value: arg0})
var err error = PanicError{stack: debugStack(caller), Value: arg0}
if interp.ctx.panicFunc != nil {
err = interp.ctx.handlePanic(caller, fn, err)
}
panic(err)

case "recover":
caller.setReg(ir, doRecover(caller))
Expand Down Expand Up @@ -608,7 +620,7 @@ func (interp *Interp) callBuiltinByStack(caller *frame, fn string, ssaArgs []ssa
}
caller.setReg(ir, v.Interface())
default:
panic("unknown built-in: " + fn)
panic("unknown built-in: " + fname)
}
}

Expand Down
20 changes: 20 additions & 0 deletions context.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ type Context struct {
Lookup func(root, path string) (dir string, found bool) // lookup external import
evalCallFn func(interp *Interp, call *ssa.Call, res ...interface{}) // internal eval func for repl
debugFunc func(*DebugInfo) // debug func
panicFunc func(*PanicInfo) // panic func
pkgs map[string]*SourcePackage // imports
override map[string]reflect.Value // override function
evalInit map[string]bool // eval init check
Expand Down Expand Up @@ -212,6 +213,25 @@ func (ctx *Context) SetDebug(fn func(*DebugInfo)) {
ctx.debugFunc = fn
}

func (ctx *Context) SetPanic(fn func(*PanicInfo)) {
ctx.panicFunc = fn
}

type funcInstr interface {
Parent() *ssa.Function
Pos() token.Pos
String() string
}

func (ctx *Context) handlePanic(fr *frame, fn funcInstr, err error) error {
info := &PanicInfo{funcInstr: fn, fset: ctx.FileSet, Error: err}
ctx.panicFunc(info)
if info.Error != nil {
return info.Error
}
return err
}

// RegisterExternal register external value must variable address or func.
func (ctx *Context) RegisterExternal(key string, i interface{}) {
if i == nil {
Expand Down
10 changes: 10 additions & 0 deletions interp.go
Original file line number Diff line number Diff line change
Expand Up @@ -594,6 +594,16 @@ func (i *DebugInfo) AsFunc() (*types.Func, bool) {
return v, ok
}

type PanicInfo struct {
funcInstr
fset *token.FileSet
Error error // PanicError
}

func (i *PanicInfo) Position() token.Position {
return i.fset.Position(i.Pos())
}

// prepareCall determines the function value and argument values for a
// function call in a Call, Go or Defer instruction, performing
// interface method lookup if needed.
Expand Down
8 changes: 7 additions & 1 deletion opblock.go
Original file line number Diff line number Diff line change
Expand Up @@ -911,6 +911,12 @@ func makeInstr(interp *Interp, pfn *function, instr ssa.Instruction) func(fr *fr
}
case *ssa.Panic:
ix := pfn.regIndex(instr.X)
if interp.ctx.panicFunc != nil {
return func(fr *frame) {
var err error = PanicError{stack: debugStack(fr), Value: fr.reg(ix)}
panic(interp.ctx.handlePanic(fr, instr, err))
}
}
return func(fr *frame) {
panic(PanicError{stack: debugStack(fr), Value: fr.reg(ix)})
}
Expand Down Expand Up @@ -1070,7 +1076,7 @@ func makeCallInstr(pfn *function, interp *Interp, instr ssa.Value, call *ssa.Cal
case *ssa.Builtin:
fname := fn.Name()
return func(fr *frame) {
interp.callBuiltinByStack(fr, fname, call.Args, ir, ia)
interp.callBuiltinByStack(fr, fname, fn, call.Args, ir, ia)
}
case *ssa.MakeClosure:
ifn := interp.loadFunction(fn.Fn.(*ssa.Function))
Expand Down

0 comments on commit 49008cc

Please sign in to comment.