Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ctx.SetPanic: support handle runtimeError/plainError #296

Merged
merged 2 commits into from
Feb 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
2 changes: 1 addition & 1 deletion context.go
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ type PanicInfo struct {
funcInstr
*Frame
fset *token.FileSet
Error error // PanicError
Error error // PanicError / FatalError / PlainError / RuntimeError
}

func (i *PanicInfo) Position() token.Position {
Expand Down
50 changes: 44 additions & 6 deletions errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package igop

import (
"bytes"
"errors"
"fmt"
)
Expand All @@ -36,18 +37,55 @@ func (r ExitError) Error() string {
return fmt.Sprintf("exit %v", int(r))
}

type plainError string
type PlainError string

func (e plainError) RuntimeError() {}
func (e PlainError) RuntimeError() {}

func (e plainError) Error() string {
func (e PlainError) Error() string {
return string(e)
}

type runtimeError string
type RuntimeError string

func (e runtimeError) RuntimeError() {}
func (e RuntimeError) RuntimeError() {}

func (e runtimeError) Error() string {
func (e RuntimeError) Error() string {
return "runtime error: " + string(e)
}

// If the target program panics, the interpreter panics with this type.
type PanicError struct {
stack []byte
Value value
}

func (p PanicError) Error() string {
var buf bytes.Buffer
writeany(&buf, p.Value)
return buf.String()
}

func (p PanicError) Stack() []byte {
return p.stack
}

// run func fatal error
type FatalError struct {
stack []byte
Value value
}

func (p FatalError) Error() string {
var buf bytes.Buffer
writeany(&buf, p.Value)
return buf.String()
}

func (p FatalError) Stack() []byte {
return p.stack
}

// If the target program calls exit, the interpreter panics with this type.
type exitPanic int

type goexitPanic int
23 changes: 19 additions & 4 deletions interp.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ import (
"go/token"
"go/types"
"reflect"
"runtime"
"strings"
"sync"
"sync/atomic"
Expand Down Expand Up @@ -480,6 +479,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 @@ -1277,16 +1290,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
default:
// runtimeError / plainError ...
pfr := fr
for pfr.callee != nil {
pfr = pfr.callee
}
err = PanicError{stack: debugStack(pfr), Value: p}
err = FatalError{stack: debugStack(pfr), Value: p}
if i.ctx.panicFunc != nil {
i.ctx.handlePanic(fr, i.mainpkg.Func(name), err)
}
}
}()
if fn := i.mainpkg.Func(name); fn != nil {
Expand Down
Loading
Loading