From 49008cc00d7016c72c8711e4c9a519553ec2b300 Mon Sep 17 00:00:00 2001 From: visualfc Date: Thu, 9 Jan 2025 09:05:04 +0800 Subject: [PATCH 1/3] Context.SetPanic --- builtin.go | 26 +++++++++++++++++++------- context.go | 20 ++++++++++++++++++++ interp.go | 10 ++++++++++ opblock.go | 8 +++++++- 4 files changed, 56 insertions(+), 8 deletions(-) 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/context.go b/context.go index 210c7788..bdfbdf2e 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,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 { diff --git a/interp.go b/interp.go index a651529c..2234492a 100644 --- a/interp.go +++ b/interp.go @@ -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. 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)) From 81d11a3f9a011b26606632c2009c5b086422d5ee Mon Sep 17 00:00:00 2001 From: visualfc Date: Thu, 9 Jan 2025 09:46:38 +0800 Subject: [PATCH 2/3] igoptest: skip 69110 --- cmd/igoptest/main.go | 1 + 1 file changed, 1 insertion(+) 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" From 67fc5137200fe25bb75e0ba5517c35013cc09e48 Mon Sep 17 00:00:00 2001 From: visualfc Date: Thu, 9 Jan 2025 10:21:33 +0800 Subject: [PATCH 3/3] PanicInfo.CallerFrames --- context.go | 27 ++++++++++++++++++++++++++- interp.go | 10 ---------- 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/context.go b/context.go index bdfbdf2e..4147e409 100644 --- a/context.go +++ b/context.go @@ -217,6 +217,31 @@ 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 @@ -224,7 +249,7 @@ type funcInstr interface { } func (ctx *Context) handlePanic(fr *frame, fn funcInstr, err error) error { - info := &PanicInfo{funcInstr: fn, fset: ctx.FileSet, Error: err} + info := &PanicInfo{funcInstr: fn, fset: ctx.FileSet, fr: fr, Error: err} ctx.panicFunc(info) if info.Error != nil { return info.Error diff --git a/interp.go b/interp.go index 2234492a..a651529c 100644 --- a/interp.go +++ b/interp.go @@ -594,16 +594,6 @@ 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.