314 lines
		
	
	
		
			8.2 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			314 lines
		
	
	
		
			8.2 KiB
		
	
	
	
		
			Go
		
	
	
	
// Copyright 2013 The Go Authors. All rights reserved.
 | 
						|
// Use of this source code is governed by a BSD-style
 | 
						|
// license that can be found in the LICENSE file.
 | 
						|
 | 
						|
package pointer
 | 
						|
 | 
						|
import (
 | 
						|
	"bytes"
 | 
						|
	"fmt"
 | 
						|
	"go/types"
 | 
						|
	"log"
 | 
						|
	"os"
 | 
						|
	"os/exec"
 | 
						|
	"runtime"
 | 
						|
	"time"
 | 
						|
 | 
						|
	"golang.org/x/tools/container/intsets"
 | 
						|
)
 | 
						|
 | 
						|
// CanPoint reports whether the type T is pointerlike,
 | 
						|
// for the purposes of this analysis.
 | 
						|
func CanPoint(T types.Type) bool {
 | 
						|
	switch T := T.(type) {
 | 
						|
	case *types.Named:
 | 
						|
		if obj := T.Obj(); obj.Name() == "Value" && obj.Pkg().Path() == "reflect" {
 | 
						|
			return true // treat reflect.Value like interface{}
 | 
						|
		}
 | 
						|
		return CanPoint(T.Underlying())
 | 
						|
	case *types.Pointer, *types.Interface, *types.Map, *types.Chan, *types.Signature, *types.Slice:
 | 
						|
		return true
 | 
						|
	}
 | 
						|
 | 
						|
	return false // array struct tuple builtin basic
 | 
						|
}
 | 
						|
 | 
						|
// CanHaveDynamicTypes reports whether the type T can "hold" dynamic types,
 | 
						|
// i.e. is an interface (incl. reflect.Type) or a reflect.Value.
 | 
						|
//
 | 
						|
func CanHaveDynamicTypes(T types.Type) bool {
 | 
						|
	switch T := T.(type) {
 | 
						|
	case *types.Named:
 | 
						|
		if obj := T.Obj(); obj.Name() == "Value" && obj.Pkg().Path() == "reflect" {
 | 
						|
			return true // reflect.Value
 | 
						|
		}
 | 
						|
		return CanHaveDynamicTypes(T.Underlying())
 | 
						|
	case *types.Interface:
 | 
						|
		return true
 | 
						|
	}
 | 
						|
	return false
 | 
						|
}
 | 
						|
 | 
						|
func isInterface(T types.Type) bool { return types.IsInterface(T) }
 | 
						|
 | 
						|
// mustDeref returns the element type of its argument, which must be a
 | 
						|
// pointer; panic ensues otherwise.
 | 
						|
func mustDeref(typ types.Type) types.Type {
 | 
						|
	return typ.Underlying().(*types.Pointer).Elem()
 | 
						|
}
 | 
						|
 | 
						|
// deref returns a pointer's element type; otherwise it returns typ.
 | 
						|
func deref(typ types.Type) types.Type {
 | 
						|
	if p, ok := typ.Underlying().(*types.Pointer); ok {
 | 
						|
		return p.Elem()
 | 
						|
	}
 | 
						|
	return typ
 | 
						|
}
 | 
						|
 | 
						|
// A fieldInfo describes one subelement (node) of the flattening-out
 | 
						|
// of a type T: the subelement's type and its path from the root of T.
 | 
						|
//
 | 
						|
// For example, for this type:
 | 
						|
//     type line struct{ points []struct{x, y int} }
 | 
						|
// flatten() of the inner struct yields the following []fieldInfo:
 | 
						|
//    struct{ x, y int }                      ""
 | 
						|
//    int                                     ".x"
 | 
						|
//    int                                     ".y"
 | 
						|
// and flatten(line) yields:
 | 
						|
//    struct{ points []struct{x, y int} }     ""
 | 
						|
//    struct{ x, y int }                      ".points[*]"
 | 
						|
//    int                                     ".points[*].x
 | 
						|
//    int                                     ".points[*].y"
 | 
						|
//
 | 
						|
type fieldInfo struct {
 | 
						|
	typ types.Type
 | 
						|
 | 
						|
	// op and tail describe the path to the element (e.g. ".a#2.b[*].c").
 | 
						|
	op   interface{} // *Array: true; *Tuple: int; *Struct: *types.Var; *Named: nil
 | 
						|
	tail *fieldInfo
 | 
						|
}
 | 
						|
 | 
						|
// path returns a user-friendly string describing the subelement path.
 | 
						|
//
 | 
						|
func (fi *fieldInfo) path() string {
 | 
						|
	var buf bytes.Buffer
 | 
						|
	for p := fi; p != nil; p = p.tail {
 | 
						|
		switch op := p.op.(type) {
 | 
						|
		case bool:
 | 
						|
			fmt.Fprintf(&buf, "[*]")
 | 
						|
		case int:
 | 
						|
			fmt.Fprintf(&buf, "#%d", op)
 | 
						|
		case *types.Var:
 | 
						|
			fmt.Fprintf(&buf, ".%s", op.Name())
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return buf.String()
 | 
						|
}
 | 
						|
 | 
						|
// flatten returns a list of directly contained fields in the preorder
 | 
						|
// traversal of the type tree of t.  The resulting elements are all
 | 
						|
// scalars (basic types or pointerlike types), except for struct/array
 | 
						|
// "identity" nodes, whose type is that of the aggregate.
 | 
						|
//
 | 
						|
// reflect.Value is considered pointerlike, similar to interface{}.
 | 
						|
//
 | 
						|
// Callers must not mutate the result.
 | 
						|
//
 | 
						|
func (a *analysis) flatten(t types.Type) []*fieldInfo {
 | 
						|
	fl, ok := a.flattenMemo[t]
 | 
						|
	if !ok {
 | 
						|
		switch t := t.(type) {
 | 
						|
		case *types.Named:
 | 
						|
			u := t.Underlying()
 | 
						|
			if isInterface(u) {
 | 
						|
				// Debuggability hack: don't remove
 | 
						|
				// the named type from interfaces as
 | 
						|
				// they're very verbose.
 | 
						|
				fl = append(fl, &fieldInfo{typ: t})
 | 
						|
			} else {
 | 
						|
				fl = a.flatten(u)
 | 
						|
			}
 | 
						|
 | 
						|
		case *types.Basic,
 | 
						|
			*types.Signature,
 | 
						|
			*types.Chan,
 | 
						|
			*types.Map,
 | 
						|
			*types.Interface,
 | 
						|
			*types.Slice,
 | 
						|
			*types.Pointer:
 | 
						|
			fl = append(fl, &fieldInfo{typ: t})
 | 
						|
 | 
						|
		case *types.Array:
 | 
						|
			fl = append(fl, &fieldInfo{typ: t}) // identity node
 | 
						|
			for _, fi := range a.flatten(t.Elem()) {
 | 
						|
				fl = append(fl, &fieldInfo{typ: fi.typ, op: true, tail: fi})
 | 
						|
			}
 | 
						|
 | 
						|
		case *types.Struct:
 | 
						|
			fl = append(fl, &fieldInfo{typ: t}) // identity node
 | 
						|
			for i, n := 0, t.NumFields(); i < n; i++ {
 | 
						|
				f := t.Field(i)
 | 
						|
				for _, fi := range a.flatten(f.Type()) {
 | 
						|
					fl = append(fl, &fieldInfo{typ: fi.typ, op: f, tail: fi})
 | 
						|
				}
 | 
						|
			}
 | 
						|
 | 
						|
		case *types.Tuple:
 | 
						|
			// No identity node: tuples are never address-taken.
 | 
						|
			n := t.Len()
 | 
						|
			if n == 1 {
 | 
						|
				// Don't add a fieldInfo link for singletons,
 | 
						|
				// e.g. in params/results.
 | 
						|
				fl = append(fl, a.flatten(t.At(0).Type())...)
 | 
						|
			} else {
 | 
						|
				for i := 0; i < n; i++ {
 | 
						|
					f := t.At(i)
 | 
						|
					for _, fi := range a.flatten(f.Type()) {
 | 
						|
						fl = append(fl, &fieldInfo{typ: fi.typ, op: i, tail: fi})
 | 
						|
					}
 | 
						|
				}
 | 
						|
			}
 | 
						|
 | 
						|
		default:
 | 
						|
			panic(fmt.Sprintf("cannot flatten unsupported type %T", t))
 | 
						|
		}
 | 
						|
 | 
						|
		a.flattenMemo[t] = fl
 | 
						|
	}
 | 
						|
 | 
						|
	return fl
 | 
						|
}
 | 
						|
 | 
						|
// sizeof returns the number of pointerlike abstractions (nodes) in the type t.
 | 
						|
func (a *analysis) sizeof(t types.Type) uint32 {
 | 
						|
	return uint32(len(a.flatten(t)))
 | 
						|
}
 | 
						|
 | 
						|
// shouldTrack reports whether object type T contains (recursively)
 | 
						|
// any fields whose addresses should be tracked.
 | 
						|
func (a *analysis) shouldTrack(T types.Type) bool {
 | 
						|
	if a.track == trackAll {
 | 
						|
		return true // fast path
 | 
						|
	}
 | 
						|
	track, ok := a.trackTypes[T]
 | 
						|
	if !ok {
 | 
						|
		a.trackTypes[T] = true // break cycles conservatively
 | 
						|
		// NB: reflect.Value, reflect.Type are pre-populated to true.
 | 
						|
		for _, fi := range a.flatten(T) {
 | 
						|
			switch ft := fi.typ.Underlying().(type) {
 | 
						|
			case *types.Interface, *types.Signature:
 | 
						|
				track = true // needed for callgraph
 | 
						|
			case *types.Basic:
 | 
						|
				// no-op
 | 
						|
			case *types.Chan:
 | 
						|
				track = a.track&trackChan != 0 || a.shouldTrack(ft.Elem())
 | 
						|
			case *types.Map:
 | 
						|
				track = a.track&trackMap != 0 || a.shouldTrack(ft.Key()) || a.shouldTrack(ft.Elem())
 | 
						|
			case *types.Slice:
 | 
						|
				track = a.track&trackSlice != 0 || a.shouldTrack(ft.Elem())
 | 
						|
			case *types.Pointer:
 | 
						|
				track = a.track&trackPtr != 0 || a.shouldTrack(ft.Elem())
 | 
						|
			case *types.Array, *types.Struct:
 | 
						|
				// No need to look at field types since they will follow (flattened).
 | 
						|
			default:
 | 
						|
				// Includes *types.Tuple, which are never address-taken.
 | 
						|
				panic(ft)
 | 
						|
			}
 | 
						|
			if track {
 | 
						|
				break
 | 
						|
			}
 | 
						|
		}
 | 
						|
		a.trackTypes[T] = track
 | 
						|
		if !track && a.log != nil {
 | 
						|
			fmt.Fprintf(a.log, "\ttype not tracked: %s\n", T)
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return track
 | 
						|
}
 | 
						|
 | 
						|
// offsetOf returns the (abstract) offset of field index within struct
 | 
						|
// or tuple typ.
 | 
						|
func (a *analysis) offsetOf(typ types.Type, index int) uint32 {
 | 
						|
	var offset uint32
 | 
						|
	switch t := typ.Underlying().(type) {
 | 
						|
	case *types.Tuple:
 | 
						|
		for i := 0; i < index; i++ {
 | 
						|
			offset += a.sizeof(t.At(i).Type())
 | 
						|
		}
 | 
						|
	case *types.Struct:
 | 
						|
		offset++ // the node for the struct itself
 | 
						|
		for i := 0; i < index; i++ {
 | 
						|
			offset += a.sizeof(t.Field(i).Type())
 | 
						|
		}
 | 
						|
	default:
 | 
						|
		panic(fmt.Sprintf("offsetOf(%s : %T)", typ, typ))
 | 
						|
	}
 | 
						|
	return offset
 | 
						|
}
 | 
						|
 | 
						|
// sliceToArray returns the type representing the arrays to which
 | 
						|
// slice type slice points.
 | 
						|
func sliceToArray(slice types.Type) *types.Array {
 | 
						|
	return types.NewArray(slice.Underlying().(*types.Slice).Elem(), 1)
 | 
						|
}
 | 
						|
 | 
						|
// Node set -------------------------------------------------------------------
 | 
						|
 | 
						|
type nodeset struct {
 | 
						|
	intsets.Sparse
 | 
						|
}
 | 
						|
 | 
						|
func (ns *nodeset) String() string {
 | 
						|
	var buf bytes.Buffer
 | 
						|
	buf.WriteRune('{')
 | 
						|
	var space [50]int
 | 
						|
	for i, n := range ns.AppendTo(space[:0]) {
 | 
						|
		if i > 0 {
 | 
						|
			buf.WriteString(", ")
 | 
						|
		}
 | 
						|
		buf.WriteRune('n')
 | 
						|
		fmt.Fprintf(&buf, "%d", n)
 | 
						|
	}
 | 
						|
	buf.WriteRune('}')
 | 
						|
	return buf.String()
 | 
						|
}
 | 
						|
 | 
						|
func (ns *nodeset) add(n nodeid) bool {
 | 
						|
	return ns.Sparse.Insert(int(n))
 | 
						|
}
 | 
						|
 | 
						|
func (x *nodeset) addAll(y *nodeset) bool {
 | 
						|
	return x.UnionWith(&y.Sparse)
 | 
						|
}
 | 
						|
 | 
						|
// Profiling & debugging -------------------------------------------------------
 | 
						|
 | 
						|
var timers = make(map[string]time.Time)
 | 
						|
 | 
						|
func start(name string) {
 | 
						|
	if debugTimers {
 | 
						|
		timers[name] = time.Now()
 | 
						|
		log.Printf("%s...\n", name)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func stop(name string) {
 | 
						|
	if debugTimers {
 | 
						|
		log.Printf("%s took %s\n", name, time.Since(timers[name]))
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// diff runs the command "diff a b" and reports its success.
 | 
						|
func diff(a, b string) bool {
 | 
						|
	var cmd *exec.Cmd
 | 
						|
	switch runtime.GOOS {
 | 
						|
	case "plan9":
 | 
						|
		cmd = exec.Command("/bin/diff", "-c", a, b)
 | 
						|
	default:
 | 
						|
		cmd = exec.Command("/usr/bin/diff", "-u", a, b)
 | 
						|
	}
 | 
						|
	cmd.Stdout = os.Stderr
 | 
						|
	cmd.Stderr = os.Stderr
 | 
						|
	return cmd.Run() == nil
 | 
						|
}
 |