github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/tools/go/pointer/util.go (about)

     1  // Copyright 2013 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // +build go1.5
     6  
     7  package pointer
     8  
     9  import (
    10  	"bytes"
    11  	"fmt"
    12  	"go/types"
    13  	"log"
    14  	"os"
    15  	"os/exec"
    16  	"runtime"
    17  	"time"
    18  
    19  	"golang.org/x/tools/container/intsets"
    20  )
    21  
    22  // CanPoint reports whether the type T is pointerlike,
    23  // for the purposes of this analysis.
    24  func CanPoint(T types.Type) bool {
    25  	switch T := T.(type) {
    26  	case *types.Named:
    27  		if obj := T.Obj(); obj.Name() == "Value" && obj.Pkg().Path() == "reflect" {
    28  			return true // treat reflect.Value like interface{}
    29  		}
    30  		return CanPoint(T.Underlying())
    31  
    32  	case *types.Pointer, *types.Interface, *types.Map, *types.Chan, *types.Signature, *types.Slice:
    33  		return true
    34  	}
    35  
    36  	return false // array struct tuple builtin basic
    37  }
    38  
    39  // CanHaveDynamicTypes reports whether the type T can "hold" dynamic types,
    40  // i.e. is an interface (incl. reflect.Type) or a reflect.Value.
    41  //
    42  func CanHaveDynamicTypes(T types.Type) bool {
    43  	switch T := T.(type) {
    44  	case *types.Named:
    45  		if obj := T.Obj(); obj.Name() == "Value" && obj.Pkg().Path() == "reflect" {
    46  			return true // reflect.Value
    47  		}
    48  		return CanHaveDynamicTypes(T.Underlying())
    49  	case *types.Interface:
    50  		return true
    51  	}
    52  	return false
    53  }
    54  
    55  func isInterface(T types.Type) bool { return types.IsInterface(T) }
    56  
    57  // mustDeref returns the element type of its argument, which must be a
    58  // pointer; panic ensues otherwise.
    59  func mustDeref(typ types.Type) types.Type {
    60  	return typ.Underlying().(*types.Pointer).Elem()
    61  }
    62  
    63  // deref returns a pointer's element type; otherwise it returns typ.
    64  func deref(typ types.Type) types.Type {
    65  	if p, ok := typ.Underlying().(*types.Pointer); ok {
    66  		return p.Elem()
    67  	}
    68  	return typ
    69  }
    70  
    71  // A fieldInfo describes one subelement (node) of the flattening-out
    72  // of a type T: the subelement's type and its path from the root of T.
    73  //
    74  // For example, for this type:
    75  //     type line struct{ points []struct{x, y int} }
    76  // flatten() of the inner struct yields the following []fieldInfo:
    77  //    struct{ x, y int }                      ""
    78  //    int                                     ".x"
    79  //    int                                     ".y"
    80  // and flatten(line) yields:
    81  //    struct{ points []struct{x, y int} }     ""
    82  //    struct{ x, y int }                      ".points[*]"
    83  //    int                                     ".points[*].x
    84  //    int                                     ".points[*].y"
    85  //
    86  type fieldInfo struct {
    87  	typ types.Type
    88  
    89  	// op and tail describe the path to the element (e.g. ".a#2.b[*].c").
    90  	op   interface{} // *Array: true; *Tuple: int; *Struct: *types.Var; *Named: nil
    91  	tail *fieldInfo
    92  }
    93  
    94  // path returns a user-friendly string describing the subelement path.
    95  //
    96  func (fi *fieldInfo) path() string {
    97  	var buf bytes.Buffer
    98  	for p := fi; p != nil; p = p.tail {
    99  		switch op := p.op.(type) {
   100  		case bool:
   101  			fmt.Fprintf(&buf, "[*]")
   102  		case int:
   103  			fmt.Fprintf(&buf, "#%d", op)
   104  		case *types.Var:
   105  			fmt.Fprintf(&buf, ".%s", op.Name())
   106  		}
   107  	}
   108  	return buf.String()
   109  }
   110  
   111  // flatten returns a list of directly contained fields in the preorder
   112  // traversal of the type tree of t.  The resulting elements are all
   113  // scalars (basic types or pointerlike types), except for struct/array
   114  // "identity" nodes, whose type is that of the aggregate.
   115  //
   116  // reflect.Value is considered pointerlike, similar to interface{}.
   117  //
   118  // Callers must not mutate the result.
   119  //
   120  func (a *analysis) flatten(t types.Type) []*fieldInfo {
   121  	fl, ok := a.flattenMemo[t]
   122  	if !ok {
   123  		switch t := t.(type) {
   124  		case *types.Named:
   125  			u := t.Underlying()
   126  			if isInterface(u) {
   127  				// Debuggability hack: don't remove
   128  				// the named type from interfaces as
   129  				// they're very verbose.
   130  				fl = append(fl, &fieldInfo{typ: t})
   131  			} else {
   132  				fl = a.flatten(u)
   133  			}
   134  
   135  		case *types.Basic,
   136  			*types.Signature,
   137  			*types.Chan,
   138  			*types.Map,
   139  			*types.Interface,
   140  			*types.Slice,
   141  			*types.Pointer:
   142  			fl = append(fl, &fieldInfo{typ: t})
   143  
   144  		case *types.Array:
   145  			fl = append(fl, &fieldInfo{typ: t}) // identity node
   146  			for _, fi := range a.flatten(t.Elem()) {
   147  				fl = append(fl, &fieldInfo{typ: fi.typ, op: true, tail: fi})
   148  			}
   149  
   150  		case *types.Struct:
   151  			fl = append(fl, &fieldInfo{typ: t}) // identity node
   152  			for i, n := 0, t.NumFields(); i < n; i++ {
   153  				f := t.Field(i)
   154  				for _, fi := range a.flatten(f.Type()) {
   155  					fl = append(fl, &fieldInfo{typ: fi.typ, op: f, tail: fi})
   156  				}
   157  			}
   158  
   159  		case *types.Tuple:
   160  			// No identity node: tuples are never address-taken.
   161  			n := t.Len()
   162  			if n == 1 {
   163  				// Don't add a fieldInfo link for singletons,
   164  				// e.g. in params/results.
   165  				fl = append(fl, a.flatten(t.At(0).Type())...)
   166  			} else {
   167  				for i := 0; i < n; i++ {
   168  					f := t.At(i)
   169  					for _, fi := range a.flatten(f.Type()) {
   170  						fl = append(fl, &fieldInfo{typ: fi.typ, op: i, tail: fi})
   171  					}
   172  				}
   173  			}
   174  
   175  		default:
   176  			panic(t)
   177  		}
   178  
   179  		a.flattenMemo[t] = fl
   180  	}
   181  
   182  	return fl
   183  }
   184  
   185  // sizeof returns the number of pointerlike abstractions (nodes) in the type t.
   186  func (a *analysis) sizeof(t types.Type) uint32 {
   187  	return uint32(len(a.flatten(t)))
   188  }
   189  
   190  // shouldTrack reports whether object type T contains (recursively)
   191  // any fields whose addresses should be tracked.
   192  func (a *analysis) shouldTrack(T types.Type) bool {
   193  	if a.track == trackAll {
   194  		return true // fast path
   195  	}
   196  	track, ok := a.trackTypes[T]
   197  	if !ok {
   198  		a.trackTypes[T] = true // break cycles conservatively
   199  		// NB: reflect.Value, reflect.Type are pre-populated to true.
   200  		for _, fi := range a.flatten(T) {
   201  			switch ft := fi.typ.Underlying().(type) {
   202  			case *types.Interface, *types.Signature:
   203  				track = true // needed for callgraph
   204  			case *types.Basic:
   205  				// no-op
   206  			case *types.Chan:
   207  				track = a.track&trackChan != 0 || a.shouldTrack(ft.Elem())
   208  			case *types.Map:
   209  				track = a.track&trackMap != 0 || a.shouldTrack(ft.Key()) || a.shouldTrack(ft.Elem())
   210  			case *types.Slice:
   211  				track = a.track&trackSlice != 0 || a.shouldTrack(ft.Elem())
   212  			case *types.Pointer:
   213  				track = a.track&trackPtr != 0 || a.shouldTrack(ft.Elem())
   214  			case *types.Array, *types.Struct:
   215  				// No need to look at field types since they will follow (flattened).
   216  			default:
   217  				// Includes *types.Tuple, which are never address-taken.
   218  				panic(ft)
   219  			}
   220  			if track {
   221  				break
   222  			}
   223  		}
   224  		a.trackTypes[T] = track
   225  		if !track && a.log != nil {
   226  			fmt.Fprintf(a.log, "\ttype not tracked: %s\n", T)
   227  		}
   228  	}
   229  	return track
   230  }
   231  
   232  // offsetOf returns the (abstract) offset of field index within struct
   233  // or tuple typ.
   234  func (a *analysis) offsetOf(typ types.Type, index int) uint32 {
   235  	var offset uint32
   236  	switch t := typ.Underlying().(type) {
   237  	case *types.Tuple:
   238  		for i := 0; i < index; i++ {
   239  			offset += a.sizeof(t.At(i).Type())
   240  		}
   241  	case *types.Struct:
   242  		offset++ // the node for the struct itself
   243  		for i := 0; i < index; i++ {
   244  			offset += a.sizeof(t.Field(i).Type())
   245  		}
   246  	default:
   247  		panic(fmt.Sprintf("offsetOf(%s : %T)", typ, typ))
   248  	}
   249  	return offset
   250  }
   251  
   252  // sliceToArray returns the type representing the arrays to which
   253  // slice type slice points.
   254  func sliceToArray(slice types.Type) *types.Array {
   255  	return types.NewArray(slice.Underlying().(*types.Slice).Elem(), 1)
   256  }
   257  
   258  // Node set -------------------------------------------------------------------
   259  
   260  type nodeset struct {
   261  	intsets.Sparse
   262  }
   263  
   264  func (ns *nodeset) String() string {
   265  	var buf bytes.Buffer
   266  	buf.WriteRune('{')
   267  	var space [50]int
   268  	for i, n := range ns.AppendTo(space[:0]) {
   269  		if i > 0 {
   270  			buf.WriteString(", ")
   271  		}
   272  		buf.WriteRune('n')
   273  		fmt.Fprintf(&buf, "%d", n)
   274  	}
   275  	buf.WriteRune('}')
   276  	return buf.String()
   277  }
   278  
   279  func (ns *nodeset) add(n nodeid) bool {
   280  	return ns.Sparse.Insert(int(n))
   281  }
   282  
   283  func (x *nodeset) addAll(y *nodeset) bool {
   284  	return x.UnionWith(&y.Sparse)
   285  }
   286  
   287  // Profiling & debugging -------------------------------------------------------
   288  
   289  var timers = make(map[string]time.Time)
   290  
   291  func start(name string) {
   292  	if debugTimers {
   293  		timers[name] = time.Now()
   294  		log.Printf("%s...\n", name)
   295  	}
   296  }
   297  
   298  func stop(name string) {
   299  	if debugTimers {
   300  		log.Printf("%s took %s\n", name, time.Since(timers[name]))
   301  	}
   302  }
   303  
   304  // diff runs the command "diff a b" and reports its success.
   305  func diff(a, b string) bool {
   306  	var cmd *exec.Cmd
   307  	switch runtime.GOOS {
   308  	case "plan9":
   309  		cmd = exec.Command("/bin/diff", "-c", a, b)
   310  	default:
   311  		cmd = exec.Command("/usr/bin/diff", "-u", a, b)
   312  	}
   313  	cmd.Stdout = os.Stderr
   314  	cmd.Stderr = os.Stderr
   315  	return cmd.Run() == nil
   316  }