func doRecover(caller *frame) Ivalue {
	if caller.i.mode&DisableRecover == 0 &&
		caller != nil && !caller.panicking &&
		caller.caller != nil && caller.caller.panicking {
		caller.caller.panicking = false
		caller.i.panicSource = caller.caller  
		p := caller.caller.panic
		caller.caller.panic = nil
		switch p := p.(type) {
		case targetPanic:
			return p.v
		case runtime.Error:
			return iface{caller.i.runtimeErrorString, p.Error()}
		case string:
			return iface{caller.i.runtimeErrorString, p}
		default:
			panic(fmt.Sprintf("unexpected panic type %T in target call to recover()", p))
		}
	}
	return iface{}
}
doRecover implements the recover() built-in.
package interp
import (
	"fmt"
	"go/token"
	"io"
	"os"
	"reflect"
	"runtime"
	"sync"
	"sync/atomic"
	"go/types"
	"golang.org/x/tools/go/ssa"
)
type continuation int
const (
	kNext continuation = iota
	kReturn
	kJump
)
type Mode uint
const (
	DisableRecover Mode = 1 << iota  
	EnableTracing                    
)
type methodSet map[string]*ssa.Function
type interpreter struct {
	osArgs             []Ivalue               
	prog               *ssa.Program           
	globals            map[ssa.Value]*Ivalue  
	mode               Mode                   
	reflectPackage     *ssa.Package           
	errorMethods       methodSet              
	rtypeMethods       methodSet              
	runtimeErrorString types.Type             
	sizes              types.Sizes            
	goroutines         int32                  
	externals *Externals  
	functions        map[*ssa.Function]functionInfo
	functionsRWMutex sync.RWMutex
	panicSource      *frame
	runtimeFunc map[uintptr]*ssa.Function  
	callTargetCache map[string]*ssa.Function  
	CapturedOutput   io.Writer
	capturedOutputMu sync.Mutex
}
type deferred struct {
	fn    Ivalue
	args  []Ivalue
	instr *ssa.Defer
	tail  *deferred
}
type frame struct {
	i                *interpreter
	caller           *frame
	fn               *ssa.Function
	block, prevBlock *ssa.BasicBlock
	iNum             int  
	env       []Ivalue  
	locals    []Ivalue
	defers    *deferred
	result    Ivalue
	panicking bool
	panic     interface{}
}
func (fr *frame) get(key ssa.Value) Ivalue {
	switch key := key.(type) {
	case nil:
		return nil
	case *ssa.Function, *ssa.Builtin:
		return key
	case *ssa.Const:
		return fr.i.constIvalue(key)
	case *ssa.Global:
		if r, ok := fr.i.globals[key]; ok {
			return r
		}
	}
	return fr.env[envEnt(fr, key)]
}
func (fr *frame) runDefer(d *deferred) {
	if fr.i.mode&EnableTracing != 0 {
		fmt.Fprintf(os.Stderr, "%s: invoking deferred function call\n",
			fr.i.prog.Fset.Position(d.instr.Pos()))
	}
	var ok bool
	defer func() {
		if !ok {
			fr.panicking = true
			fr.panic = recover()
			if fr.i.panicSource == nil {
				fr = fr.i.panicSource
			}
		}
	}()
	call(fr.i, fr, d.instr.Pos(), d.fn, d.args)
	ok = true
}
func (fr *frame) runDefers() {
	for d := fr.defers; d != nil; d = d.tail {
		fr.runDefer(d)
	}
	fr.defers = nil
	if fr.panicking {
		if fr.i.panicSource == nil {
			fr.i.panicSource = fr
		}
		panic(fr.panic)  
	}
}
func lookupMethod(i *interpreter, typ types.Type, meth *types.Func) *ssa.Function {
	switch typ {
	case rtypeType:
		return i.rtypeMethods[meth.Id()]
	case errorType:
		return i.errorMethods[meth.Id()]
	}
	return i.prog.LookupMethod(typ, meth.Pkg(), meth.Name())
}
func visitInstr(fr *frame, instr ssa.Instruction) continuation {
	if debugStats {
		debugMapMutex.Lock()
		debugMap[fmt.Sprintf("%T", instr)]++
		debugMapMutex.Unlock()
	}
	switch instr := instr.(type) {
	case *ssa.DebugRef:
	case *ssa.UnOp:
		fr.env[envEnt(fr, instr)] = fr.i.unop(instr, fr.get(instr.X))
	case *ssa.BinOp:
		fr.env[envEnt(fr, instr)] = binop(instr.Op, instr.X.Type(), fr.get(instr.X), fr.get(instr.Y))
	case *ssa.Call:
		fn, args := prepareCall(fr, &instr.Call)
		fr.env[envEnt(fr, instr)] = call(fr.i, fr, instr.Pos(), fn, args)
	case *ssa.ChangeInterface:
		fr.env[envEnt(fr, instr)] = fr.get(instr.X)
	case *ssa.ChangeType:
		fr.env[envEnt(fr, instr)] = fr.get(instr.X)  
	case *ssa.Convert:
		fr.env[envEnt(fr, instr)] = fr.i.conv(instr.Type(), instr.X.Type(), fr.get(instr.X))
	case *ssa.MakeInterface:
		fr.env[envEnt(fr, instr)] = iface{t: instr.X.Type(), v: fr.get(instr.X)}
	case *ssa.Extract:
		fr.env[envEnt(fr, instr)] = fr.get(instr.Tuple).(tuple)[instr.Index]
	case *ssa.Slice:
		fr.env[envEnt(fr, instr)] = slice(fr.get(instr.X), fr.get(instr.Low), fr.get(instr.High), fr.get(instr.Max))
	case *ssa.Return:
		switch len(instr.Results) {
		case 0:
		case 1:
			fr.result = fr.get(instr.Results[0])
		default:
			var res []Ivalue
			for _, r := range instr.Results {
				res = append(res, fr.get(r))
			}
			fr.result = tuple(res)
		}
		fr.block = nil
		return kReturn
	case *ssa.RunDefers:
		fr.runDefers()
	case *ssa.Panic:
		if fr.i.panicSource == nil {
			fr.i.panicSource = fr
		}
		panic(targetPanic{fr.get(instr.X)})
	case *ssa.Send:
		fr.get(instr.Chan).(chan Ivalue) <- fr.get(instr.X)
	case *ssa.Store:
		store(deref(instr.Addr.Type()), fr.get(instr.Addr).(*Ivalue), fr.get(instr.Val))
	case *ssa.If:
		succ := 1
		if fr.get(instr.Cond).(bool) {
			succ = 0
		}
		fr.prevBlock, fr.block = fr.block, fr.block.Succs[succ]
		return kJump
	case *ssa.Jump:
		fr.prevBlock, fr.block = fr.block, fr.block.Succs[0]
		return kJump
	case *ssa.Defer:
		fn, args := prepareCall(fr, &instr.Call)
		fr.defers = &deferred{
			fn:    fn,
			args:  args,
			instr: instr,
			tail:  fr.defers,
		}
	case *ssa.Go:
		fn, args := prepareCall(fr, &instr.Call)
		atomic.AddInt32(&fr.i.goroutines, 1)
		go func() {
			defer func() {
				fr.i.userStackIfPanic()
			}()
			call(fr.i, nil, instr.Pos(), fn, args)
			atomic.AddInt32(&fr.i.goroutines, -1)
		}()
	case *ssa.MakeChan:
		fr.env[envEnt(fr, instr)] = make(chan Ivalue, asInt(fr.get(instr.Size)))
	case *ssa.Alloc:
		var addr *Ivalue
		if instr.Heap {
			addr = new(Ivalue)
			fr.env[envEnt(fr, instr)] = addr
		} else {
			addr = fr.env[envEnt(fr, instr)].(*Ivalue)
		}
		*addr = fr.i.zero(deref(instr.Type()))
	case *ssa.MakeSlice:
		slice := make([]Ivalue, asInt(fr.get(instr.Cap)))
		tElt := instr.Type().Underlying().(*types.Slice).Elem()
		for i := range slice {
			slice[i] = fr.i.zero(tElt)
		}
		fr.env[envEnt(fr, instr)] = slice[:asInt(fr.get(instr.Len))]
	case *ssa.MakeMap:
		reserve := 0
		if instr.Reserve != nil {
			reserve = asInt(fr.get(instr.Reserve))
		}
		fr.env[envEnt(fr, instr)] = makeMap(instr.Type().Underlying().(*types.Map).Key(), reserve)
	case *ssa.Range:
		fr.env[envEnt(fr, instr)] = rangeIter(fr.get(instr.X), instr.X.Type())
	case *ssa.Next:
		fr.env[envEnt(fr, instr)] = fr.get(instr.Iter).(iter).next()
	case *ssa.FieldAddr:
		x := fr.get(instr.X)
		fr.env[envEnt(fr, instr)] = &(*x.(*Ivalue)).(structure)[instr.Field]
	case *ssa.Field:
		fr.env[envEnt(fr, instr)] = fr.get(instr.X).(structure)[instr.Field]
	case *ssa.IndexAddr:
		x := fr.get(instr.X)
		idx := fr.get(instr.Index)
		switch x := x.(type) {
		case []Ivalue:
			fr.env[envEnt(fr, instr)] = &x[asInt(idx)]
		case *Ivalue:  
			fr.env[envEnt(fr, instr)] = &(*x).(array)[asInt(idx)]
		default:
			panic(fmt.Sprintf("unexpected x type in IndexAddr: %T", x))
		}
	case *ssa.Index:
		fr.env[envEnt(fr, instr)] = fr.get(instr.X).(array)[asInt(fr.get(instr.Index))]
	case *ssa.Lookup:
		fr.env[envEnt(fr, instr)] = fr.i.lookup(instr, fr.get(instr.X), fr.get(instr.Index))
	case *ssa.MapUpdate:
		m := fr.get(instr.Map)
		key := fr.get(instr.Key)
		v := fr.get(instr.Value)
		switch m := m.(type) {
		case map[Ivalue]Ivalue:
			m[key] = v
		case *hashmap:
			m.insert(key.(hashable), v)
		default:
			panic(fmt.Sprintf("illegal map type: %T", m))
		}
	case *ssa.TypeAssert:
		fr.env[envEnt(fr, instr)] = typeAssert(fr.i, instr, fr.get(instr.X).(iface))
	case *ssa.MakeClosure:
		var bindings []Ivalue
		for _, binding := range instr.Bindings {
			bindings = append(bindings, fr.get(binding))
		}
		fr.env[envEnt(fr, instr)] = &closure{instr.Fn.(*ssa.Function), bindings}
	case *ssa.Phi:
		for i, pred := range instr.Block().Preds {
			if fr.prevBlock == pred {
				fr.env[envEnt(fr, instr)] = fr.get(instr.Edges[i])
				break
			}
		}
	case *ssa.Select:
		var cases []reflect.SelectCase
		if !instr.Blocking {
			cases = append(cases, reflect.SelectCase{
				Dir: reflect.SelectDefault,
			})
		}
		for _, state := range instr.States {
			var dir reflect.SelectDir
			if state.Dir == types.RecvOnly {
				dir = reflect.SelectRecv
			} else {
				dir = reflect.SelectSend
			}
			var send reflect.Value
			if state.Send != nil {
				send = reflect.ValueOf(fr.get(state.Send))
			}
			cases = append(cases, reflect.SelectCase{
				Dir:  dir,
				Chan: reflect.ValueOf(fr.get(state.Chan)),
				Send: send,
			})
		}
		chosen, recv, recvOk := reflect.Select(cases)
		if !instr.Blocking {
			chosen--  
		}
		r := tuple{chosen, recvOk}
		for i, st := range instr.States {
			if st.Dir == types.RecvOnly {
				var v Ivalue
				if i == chosen && recvOk {
					v = recv.Interface().(Ivalue)
				} else {
					v = fr.i.zero(st.Chan.Type().Underlying().(*types.Chan).Elem())
				}
				r = append(r, v)
			}
		}
		fr.env[envEnt(fr, instr)] = r
	default:
		panic(fmt.Sprintf("unexpected instruction: %T", instr))
	}
	return kNext
}
func prepareCall(fr *frame, call *ssa.CallCommon) (fn Ivalue, args []Ivalue) {
	v := fr.get(call.Value)
	if call.Method == nil {
		fn = v
	} else {
		recv := v.(iface)
		if recv.t == nil {
			panic("method invoked on nil interface")
		}
		if f := lookupMethod(fr.i, recv.t, call.Method); f == nil {
			panic(fmt.Sprintf("method set for dynamic type %v does not contain %s", recv.t, call.Method))
		} else {
			fn = f
		}
		args = append(args, recv.v)
	}
	for _, arg := range call.Args {
		args = append(args, fr.get(arg))
	}
	return
}
func call(i *interpreter, caller *frame, callpos token.Pos, fn Ivalue, args []Ivalue) Ivalue {
	switch fn := fn.(type) {
	case *ssa.Function:
		if fn == nil {
			panic("call of nil function")  
		}
		return callSSA(i, caller, callpos, fn, args, nil)
	case *closure:
		return callSSA(i, caller, callpos, fn.Fn, args, fn.Env)
	case *ssa.Builtin:
		return callBuiltin(caller, callpos, fn, args)
	}
	panic(fmt.Sprintf("cannot call %T", fn))
}
func loc(fset *token.FileSet, pos token.Pos) string {
	if pos == token.NoPos {
		return ""
	}
	return " at " + fset.Position(pos).String()
}
func callSSA(i *interpreter, caller *frame, callpos token.Pos, fn *ssa.Function, args []Ivalue, env []Ivalue) Ivalue {
	if i.mode&EnableTracing != 0 {
		fset := fn.Prog.Fset
		fmt.Fprintf(os.Stderr, "Entering %s%s.\n", fn, loc(fset, fn.Pos()))
		suffix := ""
		if caller != nil {
			suffix = ", resuming " + caller.fn.String() + loc(fset, callpos)
		}
		defer fmt.Fprintf(os.Stderr, "Leaving %s%s.\n", fn, suffix)
	}
	fr := &frame{
		i:      i,
		caller: caller,  
		fn:     fn,
	}
	fr.getFnInf()  
	if fn.Parent() == nil {
		name := fn.String()
		if ext := i.externals.funcs[name]; ext != nil {
			if i.mode&EnableTracing != 0 {
				fmt.Fprintln(os.Stderr, "\t(user-defined external)")
			}
			return ext(fr, args)
		}
		if ext := externals[name]; ext != nil {
			if i.mode&EnableTracing != 0 {
				fmt.Fprintln(os.Stderr, "\t(interpreter external)")
			}
			return ext(fr, args)
		}
		if fn.Blocks == nil {
			fr.i.panicSource = fr
			panic("no code for function: " + name)
		}
	}
	fr.env = make([]Ivalue, len(i.functions[fr.fn].envEntries))  
	fr.block = fn.Blocks[0]
	fr.locals = make([]Ivalue, len(fn.Locals))
	for i, l := range fn.Locals {  
		fr.locals[i] = fr.i.zero(deref(l.Type()))
		fr.env[envEnt(fr, l)] = &fr.locals[i]
	}
	for i, p := range fn.Params {
		fr.env[envEnt(fr, p)] = args[i]
	}
	for i, fv := range fn.FreeVars {
		fr.env[envEnt(fr, fv)] = env[i]
	}
	for fr.block != nil {
		runFrame(fr)
	}
	for i := range fn.Locals {
		fr.locals[i] = bad{}
	}
	return fr.result
}
func runFrame(fr *frame) {
	defer func() {
		if fr.block == nil {
			return  
		}
		if fr.i.mode&DisableRecover != 0 {
			return  
		}
		fr.panicking = true
		fr.panic = recover()
		if fr.i.panicSource == nil {
			fr.i.panicSource = fr
		}
		if fr.i.mode&EnableTracing != 0 {
			fmt.Fprintf(os.Stderr, "Panicking: %T %v.\n", fr.panic, fr.panic)
		}
		fr.runDefers()
		fr.block = fr.fn.Recover
	}()
	fnInf := fr.getFnInf()
	fnInstrs := fnInf.instrs
	blkInstrs := fnInstrs[fr.block.Index]
	trace := fr.i.mode&EnableTracing != 0
	var instr ssa.Instruction
	for {
		if trace {
			fmt.Fprintf(os.Stderr, ".%s:\n", fr.block)
		}
	block:
		for fr.iNum, instr = range fr.block.Instrs {
			if trace {
				if v, ok := instr.(ssa.Value); ok {
					fmt.Fprintln(os.Stderr, "\t", v.Name(), "=", instr)
				} else {
					fmt.Fprintln(os.Stderr, "\t", instr)
				}
			}
			if fun := blkInstrs[fr.iNum]; fun != nil {
				fun(fr)
			} else {
				switch visitInstr(fr, instr) {
				case kReturn:
					return
				case kNext:
				case kJump:
					blkInstrs = fnInstrs[fr.block.Index]
					break block
				}
			}
		}
	}
}