mirror of
https://github.com/golang/go
synced 2024-11-19 04:14:45 -07:00
7a70c382be
reflect.Values may point to tagged objects with interface type, e.g. x := reflect.ValueOf(new(interface{})).Elem(). We failed to consider this when implementing Elem. Also, (reflect.Value).Interface() must do one "unboxing" when it encounters such tagged objects. i.e., x.Elem().Interface() and x.Interface() are equivalent in that case. Also: - add example of tagged object with interface type. - untabify (Label).String docstring. - added tests. R=crawshaw CC=golang-dev https://golang.org/cl/18020044
158 lines
4.7 KiB
Go
158 lines
4.7 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)
|
|
// - instrinsic 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
|
|
}
|
|
|
|
// 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 {
|
|
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.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)
|
|
// <command-line arguments> (an instrinsic 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)
|
|
//
|
|
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.Func())
|
|
} 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()
|
|
}
|