// 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: 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 } // 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 _, 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 ------------------------------------------------------------------- // NB, mutator methods are attached to *nodeset. // nodeset may be a reference, but its address matters! 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 ------------------------------------------------------------------- const empty nodeid = 1<<32 - 1 type worklist interface { add(nodeid) // Adds a node to the set take() nodeid // Takes a node from the set and returns it, or empty } // Simple nondeterministic worklist based on a built-in map. type mapWorklist struct { set nodeset } func (w *mapWorklist) add(n nodeid) { w.set[n] = struct{}{} } func (w *mapWorklist) take() nodeid { for k := range w.set { delete(w.set, k) return k } return empty } func makeMapWorklist() worklist { return &mapWorklist{make(nodeset)} }