| // Copyright 2013 The Go Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| // Package ssa/interp defines an interpreter for the SSA | |
| // representation of Go programs. | |
| // | |
| // This interpreter is provided as an adjunct for testing the SSA | |
| // construction algorithm. Its purpose is to provide a minimal | |
| // metacircular implementation of the dynamic semantics of each SSA | |
| // instruction. It is not, and will never be, a production-quality Go | |
| // interpreter. | |
| // | |
| // The following is a partial list of Go features that are currently | |
| // unsupported or incomplete in the interpreter. | |
| // | |
| // * Unsafe operations, including all uses of unsafe.Pointer, are | |
| // impossible to support given the "boxed" value representation we | |
| // have chosen. | |
| // | |
| // * The reflect package is only partially implemented. | |
| // | |
| // * The "testing" package is no longer supported because it | |
| // depends on low-level details that change too often. | |
| // | |
| // * "sync/atomic" operations are not atomic due to the "boxed" value | |
| // representation: it is not possible to read, modify and write an | |
| // interface value atomically. As a consequence, Mutexes are currently | |
| // broken. | |
| // | |
| // * recover is only partially implemented. Also, the interpreter | |
| // makes no attempt to distinguish target panics from interpreter | |
| // crashes. | |
| // | |
| // * the sizes of the int, uint and uintptr types in the target | |
| // program are assumed to be the same as those of the interpreter | |
| // itself. | |
| // | |
| // * all values occupy space, even those of types defined by the spec | |
| // to have zero size, e.g. struct{}. This can cause asymptotic | |
| // performance degradation. | |
| // | |
| // * os.Exit is implemented using panic, causing deferred functions to | |
| // run. | |
| package interp // import "golang.org/x/tools/go/ssa/interp" | |
| import ( | |
| "fmt" | |
| "go/token" | |
| "go/types" | |
| "os" | |
| "reflect" | |
| "runtime" | |
| "sync/atomic" | |
| _ "unsafe" | |
| "golang.org/x/tools/go/ssa" | |
| "golang.org/x/tools/internal/typeparams" | |
| ) | |
| type continuation int | |
| const ( | |
| kNext continuation = iota | |
| kReturn | |
| kJump | |
| ) | |
| // Mode is a bitmask of options affecting the interpreter. | |
| type Mode uint | |
| const ( | |
| DisableRecover Mode = 1 << iota // Disable recover() in target programs; show interpreter crash instead. | |
| EnableTracing // Print a trace of all instructions as they are interpreted. | |
| ) | |
| type methodSet map[string]*ssa.Function | |
| // State shared between all interpreted goroutines. | |
| type interpreter struct { | |
| osArgs []value // the value of os.Args | |
| prog *ssa.Program // the SSA program | |
| globals map[*ssa.Global]*value // addresses of global variables (immutable) | |
| mode Mode // interpreter options | |
| reflectPackage *ssa.Package // the fake reflect package | |
| errorMethods methodSet // the method set of reflect.error, which implements the error interface. | |
| rtypeMethods methodSet // the method set of rtype, which implements the reflect.Type interface. | |
| runtimeErrorString types.Type // the runtime.errorString type | |
| sizes types.Sizes // the effective type-sizing function | |
| goroutines int32 // atomically updated | |
| } | |
| type deferred struct { | |
| fn value | |
| args []value | |
| instr *ssa.Defer | |
| tail *deferred | |
| } | |
| type frame struct { | |
| i *interpreter | |
| caller *frame | |
| fn *ssa.Function | |
| block, prevBlock *ssa.BasicBlock | |
| env map[ssa.Value]value // dynamic values of SSA variables | |
| locals []value | |
| defers *deferred | |
| result value | |
| panicking bool | |
| panic interface{} | |
| } | |
| func (fr *frame) get(key ssa.Value) value { | |
| switch key := key.(type) { | |
| case nil: | |
| // Hack; simplifies handling of optional attributes | |
| // such as ssa.Slice.{Low,High}. | |
| return nil | |
| case *ssa.Function, *ssa.Builtin: | |
| return key | |
| case *ssa.Const: | |
| return constValue(key) | |
| case *ssa.Global: | |
| if r, ok := fr.i.globals[key]; ok { | |
| return r | |
| } | |
| } | |
| if r, ok := fr.env[key]; ok { | |
| return r | |
| } | |
| panic(fmt.Sprintf("get: no value for %T: %v", key, key.Name())) | |
| } | |
| // runDefer runs a deferred call d. | |
| // It always returns normally, but may set or clear fr.panic. | |
| 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 { | |
| // Deferred call created a new state of panic. | |
| fr.panicking = true | |
| fr.panic = recover() | |
| } | |
| }() | |
| call(fr.i, fr, d.instr.Pos(), d.fn, d.args) | |
| ok = true | |
| } | |
| // runDefers executes fr's deferred function calls in LIFO order. | |
| // | |
| // On entry, fr.panicking indicates a state of panic; if | |
| // true, fr.panic contains the panic value. | |
| // | |
| // On completion, if a deferred call started a panic, or if no | |
| // deferred call recovered from a previous state of panic, then | |
| // runDefers itself panics after the last deferred call has run. | |
| // | |
| // If there was no initial state of panic, or it was recovered from, | |
| // runDefers returns normally. | |
| func (fr *frame) runDefers() { | |
| for d := fr.defers; d != nil; d = d.tail { | |
| fr.runDefer(d) | |
| } | |
| fr.defers = nil | |
| if fr.panicking { | |
| panic(fr.panic) // new panic, or still panicking | |
| } | |
| } | |
| // lookupMethod returns the method set for type typ, which may be one | |
| // of the interpreter's fake types. | |
| 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()) | |
| } | |
| // visitInstr interprets a single ssa.Instruction within the activation | |
| // record frame. It returns a continuation value indicating where to | |
| // read the next instruction from. | |
| func visitInstr(fr *frame, instr ssa.Instruction) continuation { | |
| switch instr := instr.(type) { | |
| case *ssa.DebugRef: | |
| // no-op | |
| case *ssa.UnOp: | |
| fr.env[instr] = unop(instr, fr.get(instr.X)) | |
| case *ssa.BinOp: | |
| fr.env[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[instr] = call(fr.i, fr, instr.Pos(), fn, args) | |
| case *ssa.ChangeInterface: | |
| fr.env[instr] = fr.get(instr.X) | |
| case *ssa.ChangeType: | |
| fr.env[instr] = fr.get(instr.X) // (can't fail) | |
| case *ssa.Convert: | |
| fr.env[instr] = conv(instr.Type(), instr.X.Type(), fr.get(instr.X)) | |
| case *ssa.SliceToArrayPointer: | |
| fr.env[instr] = sliceToArrayPointer(instr.Type(), instr.X.Type(), fr.get(instr.X)) | |
| case *ssa.MakeInterface: | |
| fr.env[instr] = iface{t: instr.X.Type(), v: fr.get(instr.X)} | |
| case *ssa.Extract: | |
| fr.env[instr] = fr.get(instr.Tuple).(tuple)[instr.Index] | |
| case *ssa.Slice: | |
| fr.env[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 []value | |
| 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: | |
| panic(targetPanic{fr.get(instr.X)}) | |
| case *ssa.Send: | |
| fr.get(instr.Chan).(chan value) <- fr.get(instr.X) | |
| case *ssa.Store: | |
| store(typeparams.MustDeref(instr.Addr.Type()), fr.get(instr.Addr).(*value), 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) | |
| defers := &fr.defers | |
| if into := fr.get(deferStack(instr)); into != nil { | |
| defers = into.(**deferred) | |
| } | |
| *defers = &deferred{ | |
| fn: fn, | |
| args: args, | |
| instr: instr, | |
| tail: *defers, | |
| } | |
| case *ssa.Go: | |
| fn, args := prepareCall(fr, &instr.Call) | |
| atomic.AddInt32(&fr.i.goroutines, 1) | |
| go func() { | |
| call(fr.i, nil, instr.Pos(), fn, args) | |
| atomic.AddInt32(&fr.i.goroutines, -1) | |
| }() | |
| case *ssa.MakeChan: | |
| fr.env[instr] = make(chan value, asInt64(fr.get(instr.Size))) | |
| case *ssa.Alloc: | |
| var addr *value | |
| if instr.Heap { | |
| // new | |
| addr = new(value) | |
| fr.env[instr] = addr | |
| } else { | |
| // local | |
| addr = fr.env[instr].(*value) | |
| } | |
| *addr = zero(typeparams.MustDeref(instr.Type())) | |
| case *ssa.MakeSlice: | |
| slice := make([]value, asInt64(fr.get(instr.Cap))) | |
| tElt := instr.Type().Underlying().(*types.Slice).Elem() | |
| for i := range slice { | |
| slice[i] = zero(tElt) | |
| } | |
| fr.env[instr] = slice[:asInt64(fr.get(instr.Len))] | |
| case *ssa.MakeMap: | |
| var reserve int64 | |
| if instr.Reserve != nil { | |
| reserve = asInt64(fr.get(instr.Reserve)) | |
| } | |
| if !fitsInt(reserve, fr.i.sizes) { | |
| panic(fmt.Sprintf("ssa.MakeMap.Reserve value %d does not fit in int", reserve)) | |
| } | |
| fr.env[instr] = makeMap(instr.Type().Underlying().(*types.Map).Key(), reserve) | |
| case *ssa.Range: | |
| fr.env[instr] = rangeIter(fr.get(instr.X), instr.X.Type()) | |
| case *ssa.Next: | |
| fr.env[instr] = fr.get(instr.Iter).(iter).next() | |
| case *ssa.FieldAddr: | |
| fr.env[instr] = &(*fr.get(instr.X).(*value)).(structure)[instr.Field] | |
| case *ssa.Field: | |
| fr.env[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 []value: | |
| fr.env[instr] = &x[asInt64(idx)] | |
| case *value: // *array | |
| fr.env[instr] = &(*x).(array)[asInt64(idx)] | |
| default: | |
| panic(fmt.Sprintf("unexpected x type in IndexAddr: %T", x)) | |
| } | |
| case *ssa.Index: | |
| x := fr.get(instr.X) | |
| idx := fr.get(instr.Index) | |
| switch x := x.(type) { | |
| case array: | |
| fr.env[instr] = x[asInt64(idx)] | |
| case string: | |
| fr.env[instr] = x[asInt64(idx)] | |
| default: | |
| panic(fmt.Sprintf("unexpected x type in Index: %T", x)) | |
| } | |
| case *ssa.Lookup: | |
| fr.env[instr] = 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[value]value: | |
| m[key] = v | |
| case *hashmap: | |
| m.insert(key.(hashable), v) | |
| default: | |
| panic(fmt.Sprintf("illegal map type: %T", m)) | |
| } | |
| case *ssa.TypeAssert: | |
| fr.env[instr] = typeAssert(fr.i, instr, fr.get(instr.X).(iface)) | |
| case *ssa.MakeClosure: | |
| var bindings []value | |
| for _, binding := range instr.Bindings { | |
| bindings = append(bindings, fr.get(binding)) | |
| } | |
| fr.env[instr] = &closure{instr.Fn.(*ssa.Function), bindings} | |
| case *ssa.Phi: | |
| for i, pred := range instr.Block().Preds { | |
| if fr.prevBlock == pred { | |
| fr.env[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-- // default case should have index -1. | |
| } | |
| r := tuple{chosen, recvOk} | |
| for i, st := range instr.States { | |
| if st.Dir == types.RecvOnly { | |
| var v value | |
| if i == chosen && recvOk { | |
| // No need to copy since send makes an unaliased copy. | |
| v = recv.Interface().(value) | |
| } else { | |
| v = zero(st.Chan.Type().Underlying().(*types.Chan).Elem()) | |
| } | |
| r = append(r, v) | |
| } | |
| } | |
| fr.env[instr] = r | |
| default: | |
| panic(fmt.Sprintf("unexpected instruction: %T", instr)) | |
| } | |
| // if val, ok := instr.(ssa.Value); ok { | |
| // fmt.Println(toString(fr.env[val])) // debugging | |
| // } | |
| return kNext | |
| } | |
| // 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. | |
| func prepareCall(fr *frame, call *ssa.CallCommon) (fn value, args []value) { | |
| v := fr.get(call.Value) | |
| if call.Method == nil { | |
| // Function call. | |
| fn = v | |
| } else { | |
| // Interface method invocation. | |
| recv := v.(iface) | |
| if recv.t == nil { | |
| panic("method invoked on nil interface") | |
| } | |
| if f := lookupMethod(fr.i, recv.t, call.Method); f == nil { | |
| // Unreachable in well-typed programs. | |
| 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 | |
| } | |
| // call interprets a call to a function (function, builtin or closure) | |
| // fn with arguments args, returning its result. | |
| // callpos is the position of the callsite. | |
| func call(i *interpreter, caller *frame, callpos token.Pos, fn value, args []value) value { | |
| switch fn := fn.(type) { | |
| case *ssa.Function: | |
| if fn == nil { | |
| panic("call of nil function") // nil of func type | |
| } | |
| 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() | |
| } | |
| // callSSA interprets a call to function fn with arguments args, | |
| // and lexical environment env, returning its result. | |
| // callpos is the position of the callsite. | |
| func callSSA(i *interpreter, caller *frame, callpos token.Pos, fn *ssa.Function, args []value, env []value) value { | |
| if i.mode&EnableTracing != 0 { | |
| fset := fn.Prog.Fset | |
| // TODO(adonovan): fix: loc() lies for external functions. | |
| 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, // for panic/recover | |
| fn: fn, | |
| } | |
| if fn.Parent() == nil { | |
| name := fn.String() | |
| if ext := externals[name]; ext != nil { | |
| if i.mode&EnableTracing != 0 { | |
| fmt.Fprintln(os.Stderr, "\t(external)") | |
| } | |
| return ext(fr, args) | |
| } | |
| if fn.Blocks == nil { | |
| panic("no code for function: " + name) | |
| } | |
| } | |
| // generic function body? | |
| if fn.TypeParams().Len() > 0 && len(fn.TypeArgs()) == 0 { | |
| panic("interp requires ssa.BuilderMode to include InstantiateGenerics to execute generics") | |
| } | |
| fr.env = make(map[ssa.Value]value) | |
| fr.block = fn.Blocks[0] | |
| fr.locals = make([]value, len(fn.Locals)) | |
| for i, l := range fn.Locals { | |
| fr.locals[i] = zero(typeparams.MustDeref(l.Type())) | |
| fr.env[l] = &fr.locals[i] | |
| } | |
| for i, p := range fn.Params { | |
| fr.env[p] = args[i] | |
| } | |
| for i, fv := range fn.FreeVars { | |
| fr.env[fv] = env[i] | |
| } | |
| for fr.block != nil { | |
| runFrame(fr) | |
| } | |
| // Destroy the locals to avoid accidental use after return. | |
| for i := range fn.Locals { | |
| fr.locals[i] = bad{} | |
| } | |
| return fr.result | |
| } | |
| // runFrame executes SSA instructions starting at fr.block and | |
| // continuing until a return, a panic, or a recovered panic. | |
| // | |
| // After a panic, runFrame panics. | |
| // | |
| // After a normal return, fr.result contains the result of the call | |
| // and fr.block is nil. | |
| // | |
| // A recovered panic in a function without named return parameters | |
| // (NRPs) becomes a normal return of the zero value of the function's | |
| // result type. | |
| // | |
| // After a recovered panic in a function with NRPs, fr.result is | |
| // undefined and fr.block contains the block at which to resume | |
| // control. | |
| func runFrame(fr *frame) { | |
| defer func() { | |
| if fr.block == nil { | |
| return // normal return | |
| } | |
| if fr.i.mode&DisableRecover != 0 { | |
| return // let interpreter crash | |
| } | |
| fr.panicking = true | |
| fr.panic = recover() | |
| 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 | |
| }() | |
| for { | |
| if fr.i.mode&EnableTracing != 0 { | |
| fmt.Fprintf(os.Stderr, ".%s:\n", fr.block) | |
| } | |
| block: | |
| for _, instr := range fr.block.Instrs { | |
| if fr.i.mode&EnableTracing != 0 { | |
| if v, ok := instr.(ssa.Value); ok { | |
| fmt.Fprintln(os.Stderr, "\t", v.Name(), "=", instr) | |
| } else { | |
| fmt.Fprintln(os.Stderr, "\t", instr) | |
| } | |
| } | |
| switch visitInstr(fr, instr) { | |
| case kReturn: | |
| return | |
| case kNext: | |
| // no-op | |
| case kJump: | |
| break block | |
| } | |
| } | |
| } | |
| } | |
| // doRecover implements the recover() built-in. | |
| func doRecover(caller *frame) value { | |
| // recover() must be exactly one level beneath the deferred | |
| // function (two levels beneath the panicking function) to | |
| // have any effect. Thus we ignore both "defer recover()" and | |
| // "defer f() -> g() -> recover()". | |
| if caller.i.mode&DisableRecover == 0 && | |
| caller != nil && !caller.panicking && | |
| caller.caller != nil && caller.caller.panicking { | |
| caller.caller.panicking = false | |
| p := caller.caller.panic | |
| caller.caller.panic = nil | |
| // TODO(adonovan): support runtime.Goexit. | |
| switch p := p.(type) { | |
| case targetPanic: | |
| // The target program explicitly called panic(). | |
| return p.v | |
| case runtime.Error: | |
| // The interpreter encountered a runtime error. | |
| return iface{caller.i.runtimeErrorString, p.Error()} | |
| case string: | |
| // The interpreter explicitly called panic(). | |
| return iface{caller.i.runtimeErrorString, p} | |
| default: | |
| panic(fmt.Sprintf("unexpected panic type %T in target call to recover()", p)) | |
| } | |
| } | |
| return iface{} | |
| } | |
| // Interpret interprets the Go program whose main package is mainpkg. | |
| // mode specifies various interpreter options. filename and args are | |
| // the initial values of os.Args for the target program. sizes is the | |
| // effective type-sizing function for this program. | |
| // | |
| // Interpret returns the exit code of the program: 2 for panic (like | |
| // gc does), or the argument to os.Exit for normal termination. | |
| // | |
| // The SSA program must include the "runtime" package. | |
| // | |
| // Type parameterized functions must have been built with | |
| // InstantiateGenerics in the ssa.BuilderMode to be interpreted. | |
| func Interpret(mainpkg *ssa.Package, mode Mode, sizes types.Sizes, filename string, args []string) (exitCode int) { | |
| i := &interpreter{ | |
| prog: mainpkg.Prog, | |
| globals: make(map[*ssa.Global]*value), | |
| mode: mode, | |
| sizes: sizes, | |
| goroutines: 1, | |
| } | |
| runtimePkg := i.prog.ImportedPackage("runtime") | |
| if runtimePkg == nil { | |
| panic("ssa.Program doesn't include runtime package") | |
| } | |
| i.runtimeErrorString = runtimePkg.Type("errorString").Object().Type() | |
| initReflect(i) | |
| i.osArgs = append(i.osArgs, filename) | |
| for _, arg := range args { | |
| i.osArgs = append(i.osArgs, arg) | |
| } | |
| for _, pkg := range i.prog.AllPackages() { | |
| // Initialize global storage. | |
| for _, m := range pkg.Members { | |
| switch v := m.(type) { | |
| case *ssa.Global: | |
| cell := zero(typeparams.MustDeref(v.Type())) | |
| i.globals[v] = &cell | |
| } | |
| } | |
| } | |
| // Top-level error handler. | |
| exitCode = 2 | |
| defer func() { | |
| if exitCode != 2 || i.mode&DisableRecover != 0 { | |
| return | |
| } | |
| switch p := recover().(type) { | |
| case exitPanic: | |
| exitCode = int(p) | |
| return | |
| case targetPanic: | |
| fmt.Fprintln(os.Stderr, "panic:", toString(p.v)) | |
| case runtime.Error: | |
| fmt.Fprintln(os.Stderr, "panic:", p.Error()) | |
| case string: | |
| fmt.Fprintln(os.Stderr, "panic:", p) | |
| default: | |
| fmt.Fprintf(os.Stderr, "panic: unexpected type: %T: %v\n", p, p) | |
| } | |
| // TODO(adonovan): dump panicking interpreter goroutine? | |
| // buf := make([]byte, 0x10000) | |
| // runtime.Stack(buf, false) | |
| // fmt.Fprintln(os.Stderr, string(buf)) | |
| // (Or dump panicking target goroutine?) | |
| }() | |
| // Run! | |
| call(i, nil, token.NoPos, mainpkg.Func("init"), nil) | |
| if mainFn := mainpkg.Func("main"); mainFn != nil { | |
| call(i, nil, token.NoPos, mainFn, nil) | |
| exitCode = 0 | |
| } else { | |
| fmt.Fprintln(os.Stderr, "No main function.") | |
| exitCode = 1 | |
| } | |
| return | |
| } | |
| // TODO(taking): Hack while proposal #66601 is being finalized. | |
| // | |
| //go:linkname deferStack golang.org/x/tools/go/ssa.deferStack | |
| func deferStack(i *ssa.Defer) ssa.Value |