github.com/goplus/gossa@v0.3.25/visit.go (about) 1 package gossa 2 3 import ( 4 "fmt" 5 "go/token" 6 "log" 7 "reflect" 8 "strings" 9 10 "golang.org/x/tools/go/ssa" 11 ) 12 13 func checkPackages(intp *Interp, pkgs []*ssa.Package) (err error) { 14 defer func() { 15 if v := recover(); v != nil { 16 err = v.(error) 17 } 18 }() 19 visit := visitor{ 20 intp: intp, 21 prog: intp.prog, 22 pkgs: make(map[*ssa.Package]bool), 23 seen: make(map[*ssa.Function]bool), 24 } 25 for _, pkg := range pkgs { 26 visit.pkgs[pkg] = true 27 } 28 visit.program() 29 return 30 } 31 32 type visitor struct { 33 intp *Interp 34 prog *ssa.Program 35 pkgs map[*ssa.Package]bool 36 seen map[*ssa.Function]bool 37 } 38 39 func (visit *visitor) program() { 40 chks := make(map[string]bool) 41 chks[""] = true // anonymous struct embed named type 42 for pkg := range visit.pkgs { 43 chks[pkg.Pkg.Path()] = true 44 for _, mem := range pkg.Members { 45 if fn, ok := mem.(*ssa.Function); ok { 46 visit.function(fn) 47 } 48 } 49 } 50 isExtern := func(typ reflect.Type) bool { 51 if typ.Kind() == reflect.Ptr { 52 typ = typ.Elem() 53 } 54 return !chks[typ.PkgPath()] 55 } 56 for _, T := range visit.prog.RuntimeTypes() { 57 typ := visit.intp.preToType(T) 58 // skip extern type 59 if isExtern(typ) { 60 continue 61 } 62 mmap := make(map[string]*ssa.Function) 63 mset := visit.prog.MethodSets.MethodSet(T) 64 for i, n := 0, mset.Len(); i < n; i++ { 65 sel := mset.At(i) 66 obj := sel.Obj() 67 // skip embed extern type method 68 if !chks[obj.Pkg().Path()] { 69 continue 70 } 71 fn := visit.prog.MethodValue(sel) 72 mmap[obj.Name()] = fn 73 visit.function(fn) 74 } 75 visit.intp.msets[typ] = mmap 76 } 77 } 78 79 func (visit *visitor) function(fn *ssa.Function) { 80 if visit.seen[fn] { 81 return 82 } 83 visit.seen[fn] = true 84 fnPath := fn.String() 85 if f, ok := visit.intp.ctx.override[fnPath]; ok && 86 visit.intp.preToType(fn.Type()) == f.Type() { 87 fn.Blocks = nil 88 return 89 } 90 if fn.Blocks == nil { 91 if _, ok := visit.pkgs[fn.Pkg]; ok { 92 if _, ok = externValues[fnPath]; !ok { 93 panic(fmt.Errorf("%v: missing function body", visit.intp.fset.Position(fn.Pos()))) 94 } 95 } 96 return 97 } 98 visit.intp.loadType(fn.Type()) 99 for _, alloc := range fn.Locals { 100 visit.intp.loadType(alloc.Type()) 101 visit.intp.loadType(deref(alloc.Type())) 102 } 103 pfn := visit.intp.loadFunction(fn) 104 for _, p := range fn.Params { 105 pfn.regIndex(p) 106 } 107 for _, p := range fn.FreeVars { 108 pfn.regIndex(p) 109 } 110 var buf [32]*ssa.Value // avoid alloc in common case 111 for _, b := range fn.Blocks { 112 Instrs := make([]func(*frame), len(b.Instrs), len(b.Instrs)) 113 ssaInstrs := make([]ssa.Instruction, len(b.Instrs), len(b.Instrs)) 114 var index int 115 n := len(b.Instrs) 116 for i := 0; i < n; i++ { 117 instr := b.Instrs[i] 118 ops := instr.Operands(buf[:0]) 119 switch instr := instr.(type) { 120 case *ssa.Alloc: 121 visit.intp.loadType(instr.Type()) 122 visit.intp.loadType(deref(instr.Type())) 123 case *ssa.Next: 124 // skip *ssa.opaqueType: iter 125 ops = nil 126 case *ssa.Extract: 127 // skip 128 ops = nil 129 case *ssa.TypeAssert: 130 visit.intp.loadType(instr.AssertedType) 131 case *ssa.MakeChan: 132 visit.intp.loadType(instr.Type()) 133 case *ssa.MakeMap: 134 visit.intp.loadType(instr.Type()) 135 case *ssa.MakeSlice: 136 visit.intp.loadType(instr.Type()) 137 case *ssa.SliceToArrayPointer: 138 visit.intp.loadType(instr.Type()) 139 case *ssa.Convert: 140 visit.intp.loadType(instr.Type()) 141 case *ssa.ChangeType: 142 visit.intp.loadType(instr.Type()) 143 case *ssa.MakeInterface: 144 visit.intp.loadType(instr.Type()) 145 } 146 for _, op := range ops { 147 switch v := (*op).(type) { 148 case *ssa.Function: 149 visit.function(v) 150 case nil: 151 // skip 152 default: 153 visit.intp.loadType(v.Type()) 154 } 155 } 156 ifn := makeInstr(visit.intp, pfn, instr) 157 if ifn == nil { 158 continue 159 } 160 if visit.intp.ctx.evalMode && fn.String() == "main.init" { 161 if call, ok := instr.(*ssa.Call); ok { 162 key := call.String() 163 if strings.HasPrefix(key, "init#") { 164 if visit.intp.ctx.evalInit[key] { 165 ifn = func(fr *frame) {} 166 } else { 167 visit.intp.ctx.evalInit[key] = true 168 } 169 } 170 } 171 } 172 if visit.intp.ctx.evalCallFn != nil { 173 if call, ok := instr.(*ssa.Call); ok { 174 ir := pfn.regIndex(call) 175 results := call.Call.Signature().Results() 176 ofn := ifn 177 switch results.Len() { 178 case 0: 179 ifn = func(fr *frame) { 180 ofn(fr) 181 visit.intp.ctx.evalCallFn(call) 182 } 183 case 1: 184 ifn = func(fr *frame) { 185 ofn(fr) 186 visit.intp.ctx.evalCallFn(call, fr.reg(ir)) 187 } 188 default: 189 ifn = func(fr *frame) { 190 ofn(fr) 191 r := fr.reg(ir).(tuple) 192 visit.intp.ctx.evalCallFn(call, r...) 193 } 194 } 195 } 196 } 197 if visit.intp.mode&EnableTracing != 0 { 198 ofn := ifn 199 ifn = func(fr *frame) { 200 if v, ok := instr.(ssa.Value); ok { 201 log.Printf("\t%-20T %v = %-40v\t%v\n", instr, v.Name(), instr, v.Type()) 202 } else { 203 log.Printf("\t%-20T %v\n", instr, instr) 204 } 205 ofn(fr) 206 } 207 if index == 0 { 208 ofn := ifn 209 bi := b.Index 210 ifn = func(fr *frame) { 211 log.Printf(".%v\n", bi) 212 ofn(fr) 213 } 214 } 215 if index == 0 && b.Index == 0 { 216 ofn := ifn 217 ifn = func(fr *frame) { 218 log.Printf("Entering %v%v.", fr.pfn.Fn, loc(fr.pfn.Interp.fset, fr.pfn.Fn.Pos())) 219 ofn(fr) 220 } 221 } 222 if _, ok := instr.(*ssa.Return); ok { 223 ofn := ifn 224 ifn = func(fr *frame) { 225 ofn(fr) 226 var caller ssa.Instruction 227 if fr.caller != nil { 228 caller = fr.caller.pfn.InstrForPC(fr.caller.pc - 1) 229 } 230 if caller == nil { 231 log.Printf("Leaving %v.\n", fr.pfn.Fn) 232 } else { 233 log.Printf("Leaving %v, resuming %v call %v%v.\n", 234 fr.pfn.Fn, fr.caller.pfn.Fn, caller, loc(fr.pfn.Interp.fset, caller.Pos())) 235 } 236 } 237 } 238 } 239 Instrs[index] = ifn 240 ssaInstrs[index] = instr 241 index++ 242 } 243 Instrs = Instrs[:index] 244 offset := len(pfn.Instrs) 245 pfn.Blocks = append(pfn.Blocks, offset) 246 pfn.Instrs = append(pfn.Instrs, Instrs...) 247 pfn.ssaInstrs = append(pfn.ssaInstrs, ssaInstrs...) 248 if b == fn.Recover && visit.intp.mode&DisableRecover == 0 { 249 pfn.Recover = pfn.Instrs[offset:] 250 } 251 } 252 pfn.initPool() 253 } 254 255 func loc(fset *token.FileSet, pos token.Pos) string { 256 if pos == token.NoPos { 257 return "" 258 } 259 return " at " + fset.Position(pos).String() 260 }