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  }