github.com/goplus/gossa@v0.3.25/cmd/qexp/ssa.go (about)

     1  package main
     2  
     3  import (
     4  	"fmt"
     5  	"go/build"
     6  	"go/constant"
     7  	"go/token"
     8  	"go/types"
     9  	"strings"
    10  
    11  	"golang.org/x/tools/go/loader"
    12  	"golang.org/x/tools/go/ssa"
    13  	"golang.org/x/tools/go/ssa/ssautil"
    14  )
    15  
    16  type Program struct {
    17  	prog *ssa.Program
    18  }
    19  
    20  func loadProgram(path string, ctx *build.Context) (*Program, error) {
    21  	var cfg loader.Config
    22  	cfg.Build = ctx
    23  	cfg.Import(path)
    24  
    25  	iprog, err := cfg.Load()
    26  	if err != nil {
    27  		return nil, fmt.Errorf("conf.Load failed: %s", err)
    28  	}
    29  
    30  	prog := ssautil.CreateProgram(iprog, ssa.SanityCheckFunctions|ssa.NaiveForm)
    31  	prog.Build()
    32  	return &Program{prog: prog}, nil
    33  }
    34  
    35  func (p *Program) DumpDeps(path string) {
    36  	pkg := p.prog.ImportedPackage(path)
    37  	for _, im := range pkg.Pkg.Imports() {
    38  		fmt.Println(im.Path())
    39  	}
    40  }
    41  
    42  func (p *Program) dumpDeps(path string, sep string) {
    43  	pkg := p.prog.ImportedPackage(path)
    44  	for _, im := range pkg.Pkg.Imports() {
    45  		fmt.Println(sep, im.Path())
    46  		p.dumpDeps(im.Path(), sep+"  ")
    47  	}
    48  }
    49  
    50  func (p *Program) DumpExport(path string) {
    51  	pkg := p.prog.ImportedPackage(path)
    52  	for k, v := range pkg.Members {
    53  		if token.IsExported(k) {
    54  			fmt.Printf("%v %v %T\n", k, v, v)
    55  		}
    56  	}
    57  }
    58  
    59  /*
    60  type ConstValue struct {
    61  	Typ   string
    62  	Value constant.Value
    63  }
    64  
    65  type Package struct {
    66  	Name    string
    67  	Path    string
    68  	Types   []reflect.Type
    69  	Vars    map[string]reflect.Value
    70  	Funcs   map[string]reflect.Value
    71  	Methods map[string]reflect.Value
    72  	Consts  map[string]ConstValue
    73  	Deps    map[string]string
    74  }
    75  
    76  */
    77  
    78  type Package struct {
    79  	Name          string
    80  	Path          string
    81  	Deps          []string
    82  	NamedTypes    []string
    83  	Interfaces    []string
    84  	AliasTypes    []string
    85  	Vars          []string
    86  	Funcs         []string
    87  	Methods       []string
    88  	Consts        []string
    89  	TypedConsts   []string
    90  	UntypedConsts []string
    91  }
    92  
    93  /*
    94  func unmarshalFloat(str string) constant.Value {
    95  	if sep := strings.IndexByte(str, '/'); sep >= 0 {
    96  		x := constant.MakeFromLiteral(str[:sep], token.FLOAT, 0)
    97  		y := constant.MakeFromLiteral(str[sep+1:], token.FLOAT, 0)
    98  		return constant.BinaryOp(x, token.QUO, y)
    99  	}
   100  	return constant.MakeFromLiteral(str, token.FLOAT, 0)
   101  }
   102  */
   103  
   104  func (p *Program) constToLit(named string, c constant.Value) string {
   105  	switch c.Kind() {
   106  	case constant.Bool:
   107  		if named != "" {
   108  			return fmt.Sprintf("constant.MakeBool(bool(%v))", named)
   109  		}
   110  		return fmt.Sprintf("constant.MakeBool(%v)", constant.BoolVal(c))
   111  	case constant.String:
   112  		if named != "" {
   113  			return fmt.Sprintf("constant.MakeString(string(%v))", named)
   114  		}
   115  		return fmt.Sprintf("constant.MakeString(%q)", constant.StringVal(c))
   116  	case constant.Int:
   117  		if v, ok := constant.Int64Val(c); ok {
   118  			if named != "" {
   119  				return fmt.Sprintf("constant.MakeInt64(int64(%v))", named)
   120  			}
   121  			return fmt.Sprintf("constant.MakeInt64(%v)", v)
   122  		} else if v, ok := constant.Uint64Val(c); ok {
   123  			if named != "" {
   124  				return fmt.Sprintf("constant.MakeUint64(uint64(%v))", named)
   125  			}
   126  			return fmt.Sprintf("constant.MakeUint64(%v)", v)
   127  		}
   128  		return fmt.Sprintf("constant.MakeFromLiteral(%q, token.INT, 0)", c.ExactString())
   129  	case constant.Float:
   130  		s := c.ExactString()
   131  		if pos := strings.IndexByte(s, '/'); pos >= 0 {
   132  			sx := s[:pos]
   133  			sy := s[pos+1:]
   134  			// simplify 314/100 => 3.14
   135  			// 80901699437494742410229341718281905886015458990288143106772431
   136  			// 50000000000000000000000000000000000000000000000000000000000000
   137  			if strings.HasPrefix(sy, "1") && strings.Count(sy, "0") == len(sy)-1 {
   138  				if len(sx) == len(sy) {
   139  					return fmt.Sprintf("constant.MakeFromLiteral(\"%v.%v\", token.FLOAT, 0)", sx[:1], sx[1:])
   140  				} else if len(sx) == len(sy)-1 {
   141  					return fmt.Sprintf("constant.MakeFromLiteral(\"0.%v\", token.FLOAT, 0)", sx)
   142  				} else if len(sx) < len(sy) {
   143  					return fmt.Sprintf("constant.MakeFromLiteral(\"%v.%ve-%v\", token.FLOAT, 0)", sx[:1], sx[1:], len(sy)-len(sx))
   144  				}
   145  			} else if strings.HasPrefix(sy, "5") && strings.Count(sy, "0") == len(sy)-1 {
   146  				if len(sx) == len(sy) {
   147  					c := constant.BinaryOp(constant.MakeFromLiteral(sx, token.INT, 0), token.MUL, constant.MakeInt64(2))
   148  					sx = c.ExactString()
   149  					return fmt.Sprintf("constant.MakeFromLiteral(\"%v.%v\", token.FLOAT, 0)", sx[:1], sx[1:])
   150  				}
   151  			} else if strings.HasPrefix(sx, "1") && strings.Count(sx, "0") == len(sx)-1 {
   152  				// skip
   153  			}
   154  			x := fmt.Sprintf("constant.MakeFromLiteral(%q, token.INT, 0)", sx)
   155  			y := fmt.Sprintf("constant.MakeFromLiteral(%q, token.INT, 0)", sy)
   156  			return fmt.Sprintf("constant.BinaryOp(%v, token.QUO, %v)", x, y)
   157  		}
   158  		if pos := strings.LastIndexAny(s, "123456789"); pos != -1 {
   159  			sx := s[:pos+1]
   160  			return fmt.Sprintf("constant.MakeFromLiteral(\"%v.%ve+%v\", token.FLOAT, 0)", sx[:1], sx[1:], len(s)-1)
   161  		}
   162  		return fmt.Sprintf("constant.MakeFromLiteral(%q, token.FLOAT, 0)", s)
   163  	case constant.Complex:
   164  		re := p.constToLit("", constant.Real(c))
   165  		im := p.constToLit("", constant.Imag(c))
   166  		return fmt.Sprintf("constant.BinaryOp(%v, token.ADD, constan.MakeImag(%v))", re, im)
   167  	default:
   168  		panic("unreachable")
   169  	}
   170  }
   171  
   172  func (p *Program) ExportPkg(path string, sname string) *Package {
   173  	pkg := p.prog.ImportedPackage(path)
   174  	pkgPath := pkg.Pkg.Path()
   175  	pkgName := pkg.Pkg.Name()
   176  	e := &Package{Name: pkgName, Path: pkgPath}
   177  	pkgName = sname
   178  	for _, v := range pkg.Pkg.Imports() {
   179  		e.Deps = append(e.Deps, fmt.Sprintf("%q: %q", v.Path(), v.Name()))
   180  	}
   181  	checked := make(map[ssa.Member]bool)
   182  	for k, v := range pkg.Members {
   183  		if token.IsExported(k) {
   184  			if checked[v] {
   185  				continue
   186  			}
   187  			checked[v] = true
   188  			switch t := v.(type) {
   189  			case *ssa.NamedConst:
   190  				named := pkgName + "." + t.Name()
   191  				if typ := t.Type().String(); strings.HasPrefix(typ, "untyped ") {
   192  					e.UntypedConsts = append(e.UntypedConsts, fmt.Sprintf("%q: {%q, %v}", t.Name(), t.Type().String(), p.constToLit(named, t.Value.Value)))
   193  				} else {
   194  					e.TypedConsts = append(e.TypedConsts, fmt.Sprintf("%q : {reflect.TypeOf(%v), %v}", t.Name(), pkgName+"."+t.Name(), p.constToLit(named, t.Value.Value)))
   195  				}
   196  			case *ssa.Global:
   197  				e.Vars = append(e.Vars, fmt.Sprintf("%q : reflect.ValueOf(&%v)", t.Name(), pkgName+"."+t.Name()))
   198  			case *ssa.Function:
   199  				e.Funcs = append(e.Funcs, fmt.Sprintf("%q : reflect.ValueOf(%v)", t.Name(), pkgName+"."+t.Name()))
   200  			case *ssa.Type:
   201  				typ := t.Type()
   202  				if obj, ok := t.Object().(*types.TypeName); ok && obj.IsAlias() {
   203  					name := obj.Name()
   204  					switch typ := obj.Type().(type) {
   205  					case *types.Basic:
   206  						e.AliasTypes = append(e.AliasTypes, fmt.Sprintf("%q: reflect.TypeOf((*%v)(nil)).Elem()", name, typ.Name()))
   207  					// case *types.Named:
   208  					// 	e.AliasTypes = append(e.AliasTypes, fmt.Sprintf("%q: reflect.TypeOf((*%v.%v)(nil)).Elem()", name, sname, name))
   209  					default:
   210  						e.AliasTypes = append(e.AliasTypes, fmt.Sprintf("%q: reflect.TypeOf((*%v.%v)(nil)).Elem()", name, sname, name))
   211  					}
   212  					continue
   213  				}
   214  				var ms, pms []string
   215  				recvId := typ.String()
   216  				typeName := typ.(*types.Named).Obj().Name()
   217  				methods := IntuitiveMethodSet(typ)
   218  				for _, method := range methods {
   219  					name := method.Obj().Name()
   220  					mid := method.Obj().Type().(*types.Signature).Recv().Type().String()
   221  					if mid[0] == '*' {
   222  						if mid[1:] == recvId {
   223  							pms = append(pms, name)
   224  						}
   225  					} else if mid == recvId {
   226  						ms = append(ms, name)
   227  					}
   228  					if token.IsExported(name) {
   229  						info := fmt.Sprintf("(%v).%v", method.Recv(), name)
   230  						if pkgPath == pkgName {
   231  							e.Methods = append(e.Methods, fmt.Sprintf("%q : reflect.ValueOf(%v)", info, info))
   232  						} else {
   233  							var fn string
   234  							if isPointer(method.Recv()) {
   235  								fn = fmt.Sprintf("(*%v.%v).%v", pkgName, typeName, name)
   236  							} else {
   237  								fn = fmt.Sprintf("(%v.%v).%v", pkgName, typeName, name)
   238  							}
   239  							e.Methods = append(e.Methods, fmt.Sprintf("%q: reflect.ValueOf(%v)", info, fn))
   240  						}
   241  					}
   242  				}
   243  				if types.IsInterface(typ) {
   244  					e.Interfaces = append(e.Interfaces, fmt.Sprintf("%q : reflect.TypeOf((*%v.%v)(nil)).Elem()", typeName, pkgName, typeName))
   245  				} else {
   246  					e.NamedTypes = append(e.NamedTypes, fmt.Sprintf("%q : {reflect.TypeOf((*%v.%v)(nil)).Elem(),\"%v\",\"%v\"}", typeName, pkgName, typeName,
   247  						strings.Join(ms, ","), strings.Join(pms, ",")))
   248  				}
   249  			default:
   250  				panic("unreachable")
   251  			}
   252  		}
   253  	}
   254  	return e
   255  }
   256  
   257  func (p *Program) Export(path string) (extList []string, typList []string) {
   258  	pkg := p.prog.ImportedPackage(path)
   259  	pkgPath := pkg.Pkg.Path()
   260  	pkgName := pkg.Pkg.Name()
   261  	for k, v := range pkg.Members {
   262  		if token.IsExported(k) {
   263  			switch t := v.(type) {
   264  			case *ssa.NamedConst:
   265  			case *ssa.Global:
   266  				extList = append(extList, fmt.Sprintf("%q : &%v", pkgPath+"."+t.Name(), pkgName+"."+t.Name()))
   267  			case *ssa.Function:
   268  				extList = append(extList, fmt.Sprintf("%q : %v", pkgPath+"."+t.Name(), pkgName+"."+t.Name()))
   269  			case *ssa.Type:
   270  				named := t.Type().(*types.Named)
   271  				typeName := named.Obj().Name()
   272  
   273  				typList = append(typList, fmt.Sprintf("(*%v.%v)(nil)", pkgName, typeName))
   274  				if named.Obj().Pkg() != pkg.Pkg {
   275  					continue
   276  				}
   277  				methods := IntuitiveMethodSet(t.Type())
   278  				for _, method := range methods {
   279  					name := method.Obj().Name()
   280  					if token.IsExported(name) {
   281  						info := fmt.Sprintf("(%v).%v", method.Recv(), name)
   282  						if pkgPath == pkgName {
   283  							extList = append(extList, fmt.Sprintf("%q : %v", info, info))
   284  						} else {
   285  							var fn string
   286  							if isPointer(method.Recv()) {
   287  								fn = fmt.Sprintf("(*%v.%v).%v", pkgName, typeName, name)
   288  							} else {
   289  								fn = fmt.Sprintf("(%v.%v).%v", pkgName, typeName, name)
   290  							}
   291  							extList = append(extList, fmt.Sprintf("%q : %v", info, fn))
   292  						}
   293  					}
   294  				}
   295  			}
   296  		}
   297  	}
   298  	return
   299  }
   300  
   301  func isPointer(T types.Type) bool {
   302  	_, ok := T.(*types.Pointer)
   303  	return ok
   304  }
   305  
   306  // golang.org/x/tools/go/types/typeutil.IntuitiveMethodSet
   307  func IntuitiveMethodSet(T types.Type) []*types.Selection {
   308  	isPointerToConcrete := func(T types.Type) bool {
   309  		ptr, ok := T.(*types.Pointer)
   310  		return ok && !types.IsInterface(ptr.Elem())
   311  	}
   312  
   313  	var result []*types.Selection
   314  	mset := types.NewMethodSet(T)
   315  	if types.IsInterface(T) || isPointerToConcrete(T) {
   316  		for i, n := 0, mset.Len(); i < n; i++ {
   317  			result = append(result, mset.At(i))
   318  		}
   319  	} else {
   320  		// T is some other concrete type.
   321  		// Report methods of T and *T, preferring those of T.
   322  		pmset := types.NewMethodSet(types.NewPointer(T))
   323  		for i, n := 0, pmset.Len(); i < n; i++ {
   324  			meth := pmset.At(i)
   325  			if m := mset.Lookup(meth.Obj().Pkg(), meth.Obj().Name()); m != nil {
   326  				meth = m
   327  			}
   328  			result = append(result, meth)
   329  		}
   330  	}
   331  	return result
   332  }