// 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) } if fn.Enclosing == nil { name := fn.String() if ext := externals[name]; ext != nil { if i.mode&EnableTracing != 0 { fmt.Fprintln(os.Stderr, "\t(external)") } return ext(fn, args) } if fn.Blocks == nil { panic("no code for function: " + name) } } fr := &frame{ i: i, caller: caller, // currently unused; for unwinding. fn: fn, env: make(map[ssa.Value]value), block: fn.Blocks[0], locals: make([]value, len(fn.Locals)), } for i, l := range fn.Locals { fr.locals[i] = zero(deref(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 }
// 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.Files // TODO(adonovan): fix: loc() lies for external functions. fmt.Fprintf(os.Stderr, "Entering %s%s.\n", fn.FullName(), loc(fset, fn.Pos())) suffix := "" if caller != nil { suffix = ", resuming " + caller.fn.FullName() + loc(fset, callpos) } defer fmt.Fprintf(os.Stderr, "Leaving %s%s.\n", fn.FullName(), suffix) } if fn.Enclosing == nil { name := fn.FullName() if ext := externals[name]; ext != nil { if i.mode&EnableTracing != 0 { fmt.Fprintln(os.Stderr, "\t(external)") } return ext(fn, args) } if fn.Blocks == nil { panic("no code for function: " + name) } } fr := &frame{ i: i, caller: caller, // currently unused; for unwinding. fn: fn, env: make(map[ssa.Value]value), block: fn.Blocks[0], locals: make([]value, len(fn.Locals)), } for i, l := range fn.Locals { fr.locals[i] = zero(l.Type().Deref()) 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] } var instr ssa.Instruction defer func() { if fr.status != stComplete { if fr.i.mode&DisableRecover != 0 { return // let interpreter crash } fr.status, fr.panic = stPanic, recover() } fr.rundefers() // Destroy the locals to avoid accidental use after return. for i := range fn.Locals { fr.locals[i] = bad{} } if fr.status == stPanic { panic(fr.panic) // panic stack is not entirely clean } }() for { if i.mode&EnableTracing != 0 { fmt.Fprintf(os.Stderr, ".%s:\n", fr.block) } block: for _, instr = range fr.block.Instrs { if 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: fr.status = stComplete return fr.result case kNext: // no-op case kJump: break block } } } panic("unreachable") }