| // 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" |
| |
| "code.google.com/p/go.tools/go/types" |
| ) |
| |
| // 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: |
| 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 |
| } |
| |
| // 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() |
| } |
| |
| // 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. |
| // |
| // 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 _, ok := u.(*types.Interface); ok { |
| // 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. |
| for i, n := 0, t.Len(); 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}) |
| } |
| } |
| |
| case *types.Builtin: |
| panic("flatten(*types.Builtin)") // not the type of any value |
| |
| default: |
| panic(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))) |
| } |
| |
| // 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 map[nodeid]struct{} |
| |
| // ---- Accessors ---- |
| |
| func (ns nodeset) String() string { |
| var buf bytes.Buffer |
| buf.WriteRune('{') |
| var sep string |
| for n := range ns { |
| fmt.Fprintf(&buf, "%sn%d", sep, n) |
| sep = ", " |
| } |
| buf.WriteRune('}') |
| return buf.String() |
| } |
| |
| // diff returns the set-difference x - y. nil => empty. |
| // |
| // TODO(adonovan): opt: extremely inefficient. BDDs do this in |
| // constant time. Sparse bitvectors are linear but very fast. |
| func (x nodeset) diff(y nodeset) nodeset { |
| var z nodeset |
| for k := range x { |
| if _, ok := y[k]; !ok { |
| z.add(k) |
| } |
| } |
| return z |
| } |
| |
| // clone() returns an unaliased copy of x. |
| func (x nodeset) clone() nodeset { |
| return x.diff(nil) |
| } |
| |
| // ---- Mutators ---- |
| |
| func (ns *nodeset) add(n nodeid) bool { |
| sz := len(*ns) |
| if *ns == nil { |
| *ns = make(nodeset) |
| } |
| (*ns)[n] = struct{}{} |
| return len(*ns) > sz |
| } |
| |
| func (x *nodeset) addAll(y nodeset) bool { |
| if y == nil { |
| return false |
| } |
| sz := len(*x) |
| if *x == nil { |
| *x = make(nodeset) |
| } |
| for n := range y { |
| (*x)[n] = struct{}{} |
| } |
| return len(*x) > sz |
| } |
| |
| // Constraint set ------------------------------------------------------------- |
| |
| type constraintset map[constraint]struct{} |
| |
| func (cs *constraintset) add(c constraint) bool { |
| sz := len(*cs) |
| if *cs == nil { |
| *cs = make(constraintset) |
| } |
| (*cs)[c] = struct{}{} |
| return len(*cs) > sz |
| } |
| |
| // Worklist ------------------------------------------------------------------- |
| |
| // TODO(adonovan): interface may not be general enough for certain |
| // implementations, e.g. priority queue |
| // |
| // Uses double-buffering so nodes can be added during iteration. |
| type worklist interface { |
| empty() bool // Reports whether active buffer is empty. |
| swap() bool // Switches to the shadow buffer if empty(). |
| add(nodeid) // Adds a node to the shadow buffer. |
| take() nodeid // Takes a node from the active buffer. Precondition: !empty(). |
| } |
| |
| // Horribly naive (and nondeterministic) worklist |
| // based on two hash-sets. |
| type mapWorklist struct { |
| active, shadow nodeset |
| } |
| |
| func (w *mapWorklist) empty() bool { |
| return len(w.active) == 0 |
| } |
| |
| func (w *mapWorklist) swap() bool { |
| if w.empty() { |
| w.shadow, w.active = w.active, w.shadow |
| return true |
| } |
| return false |
| } |
| |
| func (w *mapWorklist) add(n nodeid) { |
| w.shadow[n] = struct{}{} |
| } |
| |
| func (w *mapWorklist) take() nodeid { |
| for k := range w.active { |
| delete(w.active, k) |
| return k |
| } |
| panic("worklist.take(): empty active buffer") |
| } |
| |
| func makeMapWorklist() worklist { |
| return &mapWorklist{make(nodeset), make(nodeset)} |
| } |