diff --git a/builtin.go b/builtin.go index 6703584f..dd44e908 100644 --- a/builtin.go +++ b/builtin.go @@ -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) @@ -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) @@ -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]) @@ -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]) @@ -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)) @@ -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) } } diff --git a/cmd/igoptest/main.go b/cmd/igoptest/main.go index e2009173..279f6c31 100644 --- a/cmd/igoptest/main.go +++ b/cmd/igoptest/main.go @@ -96,6 +96,7 @@ func init() { gorootTestSkips["fixedbugs/issue38093.go"] = "skip js" gorootTestSkips["fixedbugs/issue64565.go"] = "skip command" gorootTestSkips["fixedbugs/issue9355.go"] = "skip command" + gorootTestSkips["fixedbugs/issue69110.go"] = "skip runtime link" gorootTestSkips["linkmain_run.go"] = "skip link" gorootTestSkips["linkobj.go"] = "skip link" gorootTestSkips["linkx_run.go"] = "skip link" diff --git a/context.go b/context.go index 210c7788..4147e409 100644 --- a/context.go +++ b/context.go @@ -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 @@ -212,6 +213,50 @@ func (ctx *Context) SetDebug(fn func(*DebugInfo)) { ctx.debugFunc = fn } +func (ctx *Context) SetPanic(fn func(*PanicInfo)) { + ctx.panicFunc = fn +} + +type PanicInfo struct { + funcInstr + fset *token.FileSet + fr *frame + Error error // PanicError +} + +func (i *PanicInfo) Position() token.Position { + return i.fset.Position(i.Pos()) +} + +func (i *PanicInfo) CallerFrames() (frames []runtime.Frame) { + rpc := make([]uintptr, 64) + n := runtimeCallers(i.fr, 1, rpc) + fs := runtime.CallersFrames(rpc[:n]) + for { + f, more := runtimeFramesNext(i.fr, fs) + frames = append(frames, f) + if !more { + break + } + } + return +} + +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, fr: fr, 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 { diff --git a/opblock.go b/opblock.go index 66d9b2d3..4f305339 100644 --- a/opblock.go +++ b/opblock.go @@ -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)}) } @@ -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))