| // 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 ( |
| "fmt" |
| "go/token" |
| "go/types" |
| "strings" |
| |
| "golang.org/x/tools/go/ssa" |
| ) |
| |
| // A Label is an entity that may be pointed to by a pointer, map, |
| // channel, 'func', slice or interface. |
| // |
| // Labels include: |
| // - functions |
| // - globals |
| // - tagged objects, representing interfaces and reflect.Values |
| // - arrays created by conversions (e.g. []byte("foo"), []byte(s)) |
| // - stack- and heap-allocated variables (including composite literals) |
| // - channels, maps and arrays created by make() |
| // - intrinsic or reflective operations that allocate (e.g. append, reflect.New) |
| // - intrinsic objects, e.g. the initial array behind os.Args. |
| // - and their subelements, e.g. "alloc.y[*].z" |
| // |
| // Labels are so varied that they defy good generalizations; |
| // some have no value, no callgraph node, or no position. |
| // Many objects have types that are inexpressible in Go: |
| // maps, channels, functions, tagged objects. |
| // |
| // At most one of Value() or ReflectType() may return non-nil. |
| // |
| type Label struct { |
| obj *object // the addressable memory location containing this label |
| subelement *fieldInfo // subelement path within obj, e.g. ".a.b[*].c" |
| } |
| |
| // Value returns the ssa.Value that allocated this label's object, if any. |
| func (l Label) Value() ssa.Value { |
| val, _ := l.obj.data.(ssa.Value) |
| return val |
| } |
| |
| // ReflectType returns the type represented by this label if it is an |
| // reflect.rtype instance object or *reflect.rtype-tagged object. |
| // |
| func (l Label) ReflectType() types.Type { |
| rtype, _ := l.obj.data.(types.Type) |
| return rtype |
| } |
| |
| // Path returns the path to the subelement of the object containing |
| // this label. For example, ".x[*].y". |
| // |
| func (l Label) Path() string { |
| return l.subelement.path() |
| } |
| |
| // Pos returns the position of this label, if known, zero otherwise. |
| func (l Label) Pos() token.Pos { |
| switch data := l.obj.data.(type) { |
| case ssa.Value: |
| return data.Pos() |
| case types.Type: |
| if nt, ok := deref(data).(*types.Named); ok { |
| return nt.Obj().Pos() |
| } |
| } |
| if cgn := l.obj.cgn; cgn != nil { |
| return cgn.fn.Pos() |
| } |
| return token.NoPos |
| } |
| |
| // String returns the printed form of this label. |
| // |
| // Examples: Object type: |
| // x (a variable) |
| // (sync.Mutex).Lock (a function) |
| // convert (array created by conversion) |
| // makemap (map allocated via make) |
| // makechan (channel allocated via make) |
| // makeinterface (tagged object allocated by makeinterface) |
| // <alloc in reflect.Zero> (allocation in instrinsic) |
| // sync.Mutex (a reflect.rtype instance) |
| // <command-line arguments> (an intrinsic object) |
| // |
| // Labels within compound objects have subelement paths: |
| // x.y[*].z (a struct variable, x) |
| // append.y[*].z (array allocated by append) |
| // makeslice.y[*].z (array allocated via make) |
| // |
| // TODO(adonovan): expose func LabelString(*types.Package, Label). |
| // |
| func (l Label) String() string { |
| var s string |
| switch v := l.obj.data.(type) { |
| case types.Type: |
| return v.String() |
| |
| case string: |
| s = v // an intrinsic object (e.g. os.Args[*]) |
| |
| case nil: |
| if l.obj.cgn != nil { |
| // allocation by intrinsic or reflective operation |
| s = fmt.Sprintf("<alloc in %s>", l.obj.cgn.fn) |
| } else { |
| s = "<unknown>" // should be unreachable |
| } |
| |
| case *ssa.Function: |
| s = v.String() |
| |
| case *ssa.Global: |
| s = v.String() |
| |
| case *ssa.Const: |
| s = v.Name() |
| |
| case *ssa.Alloc: |
| s = v.Comment |
| if s == "" { |
| s = "alloc" |
| } |
| |
| case *ssa.Call: |
| // Currently only calls to append can allocate objects. |
| if v.Call.Value.(*ssa.Builtin).Object().Name() != "append" { |
| panic("unhandled *ssa.Call label: " + v.Name()) |
| } |
| s = "append" |
| |
| case *ssa.MakeMap, *ssa.MakeChan, *ssa.MakeSlice, *ssa.Convert: |
| s = strings.ToLower(strings.TrimPrefix(fmt.Sprintf("%T", v), "*ssa.")) |
| |
| case *ssa.MakeInterface: |
| // MakeInterface is usually implicit in Go source (so |
| // Pos()==0), and tagged objects may be allocated |
| // synthetically (so no *MakeInterface data). |
| s = "makeinterface:" + v.X.Type().String() |
| |
| default: |
| panic(fmt.Sprintf("unhandled object data type: %T", v)) |
| } |
| |
| return s + l.subelement.path() |
| } |