go/pointer/labels.go
Alan Donovan 785cfaa938 go.tools/pointer: use new callgraph API.
Also: pointer.Analyze now returns a pointer.Result object,
containing the callgraph and the results of ssa.Value queries.

The oracle has been updated to use the new call and pointer APIs.

R=crawshaw, gri
CC=golang-dev
https://golang.org/cl/13915043
2013-09-25 17:17:42 -04:00

140 lines
3.9 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 (
"fmt"
"go/token"
"strings"
"code.google.com/p/go.tools/call"
"code.google.com/p/go.tools/go/types"
"code.google.com/p/go.tools/ssa"
)
// A Label is an entity that may be pointed to by a pointer, map,
// channel, 'func', slice or interface. Labels include:
//
// Labels include:
// - functions
// - globals
// - tagged objects, representing interfaces and reflect.Values
// - arrays created by literals (e.g. []byte("foo")) and conversions ([]byte(s))
// - stack- and heap-allocated variables (including composite literals)
// - channels, maps and arrays created by make()
// - instrinsic or reflective operations that allocate (e.g. append, reflect.New)
// - 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.
//
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,
// or nil if it was allocated by an intrinsic.
//
func (l Label) Value() ssa.Value {
return l.obj.val
}
// Context returns the analytic context in which this label's object was allocated,
// or nil for global objects: global, const, and shared contours for functions.
//
func (l Label) Context() call.GraphNode {
return l.obj.cgn
}
// 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 {
if v := l.Value(); v != nil {
return v.Pos()
}
if l.obj.rtype != nil {
if nt, ok := deref(l.obj.rtype).(*types.Named); ok {
return nt.Obj().Pos()
}
}
if cgn := l.obj.cgn; cgn != nil {
return cgn.Func().Pos()
}
return token.NoPos
}
// String returns the printed form of this label.
//
// Examples: Object type:
// (sync.Mutex).Lock (a function)
// "foo":[]byte (a slice constant)
// 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)
//
// 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)
//
func (l Label) String() string {
var s string
switch v := l.obj.val.(type) {
case nil:
if l.obj.rtype != nil {
return l.obj.rtype.String()
}
if l.obj.cgn != nil {
// allocation by intrinsic or reflective operation
return fmt.Sprintf("<alloc in %s>", l.obj.cgn.Func())
}
return "<unknown>" // should be unreachable
case *ssa.Function, *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 Label.val type: %T", v))
}
return s + l.subelement.path()
}