summaryrefslogtreecommitdiffhomepage
path: root/vendor/golang.org/x/tools/go/ssa/methods.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/golang.org/x/tools/go/ssa/methods.go')
-rw-r--r--vendor/golang.org/x/tools/go/ssa/methods.go281
1 files changed, 281 insertions, 0 deletions
diff --git a/vendor/golang.org/x/tools/go/ssa/methods.go b/vendor/golang.org/x/tools/go/ssa/methods.go
new file mode 100644
index 0000000..b956018
--- /dev/null
+++ b/vendor/golang.org/x/tools/go/ssa/methods.go
@@ -0,0 +1,281 @@
+// 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 ssa
+
+// This file defines utilities for population of method sets.
+
+import (
+ "fmt"
+ "go/types"
+
+ "golang.org/x/tools/go/types/typeutil"
+ "golang.org/x/tools/internal/aliases"
+)
+
+// MethodValue returns the Function implementing method sel, building
+// wrapper methods on demand. It returns nil if sel denotes an
+// interface or generic method.
+//
+// Precondition: sel.Kind() == MethodVal.
+//
+// Thread-safe.
+//
+// Acquires prog.methodsMu.
+func (prog *Program) MethodValue(sel *types.Selection) *Function {
+ if sel.Kind() != types.MethodVal {
+ panic(fmt.Sprintf("MethodValue(%s) kind != MethodVal", sel))
+ }
+ T := sel.Recv()
+ if types.IsInterface(T) {
+ return nil // interface method or type parameter
+ }
+
+ if prog.isParameterized(T) {
+ return nil // generic method
+ }
+
+ if prog.mode&LogSource != 0 {
+ defer logStack("MethodValue %s %v", T, sel)()
+ }
+
+ var b builder
+
+ m := func() *Function {
+ prog.methodsMu.Lock()
+ defer prog.methodsMu.Unlock()
+
+ // Get or create SSA method set.
+ mset, ok := prog.methodSets.At(T).(*methodSet)
+ if !ok {
+ mset = &methodSet{mapping: make(map[string]*Function)}
+ prog.methodSets.Set(T, mset)
+ }
+
+ // Get or create SSA method.
+ id := sel.Obj().Id()
+ fn, ok := mset.mapping[id]
+ if !ok {
+ obj := sel.Obj().(*types.Func)
+ needsPromotion := len(sel.Index()) > 1
+ needsIndirection := !isPointer(recvType(obj)) && isPointer(T)
+ if needsPromotion || needsIndirection {
+ fn = createWrapper(prog, toSelection(sel))
+ fn.buildshared = b.shared()
+ b.enqueue(fn)
+ } else {
+ fn = prog.objectMethod(obj, &b)
+ }
+ if fn.Signature.Recv() == nil {
+ panic(fn)
+ }
+ mset.mapping[id] = fn
+ } else {
+ b.waitForSharedFunction(fn)
+ }
+
+ return fn
+ }()
+
+ b.iterate()
+
+ return m
+}
+
+// objectMethod returns the Function for a given method symbol.
+// The symbol may be an instance of a generic function. It need not
+// belong to an existing SSA package created by a call to
+// prog.CreatePackage.
+//
+// objectMethod panics if the function is not a method.
+//
+// Acquires prog.objectMethodsMu.
+func (prog *Program) objectMethod(obj *types.Func, b *builder) *Function {
+ sig := obj.Type().(*types.Signature)
+ if sig.Recv() == nil {
+ panic("not a method: " + obj.String())
+ }
+
+ // Belongs to a created package?
+ if fn := prog.FuncValue(obj); fn != nil {
+ return fn
+ }
+
+ // Instantiation of generic?
+ if originObj := obj.Origin(); originObj != obj {
+ origin := prog.objectMethod(originObj, b)
+ assert(origin.typeparams.Len() > 0, "origin is not generic")
+ targs := receiverTypeArgs(obj)
+ return origin.instance(targs, b)
+ }
+
+ // Consult/update cache of methods created from types.Func.
+ prog.objectMethodsMu.Lock()
+ defer prog.objectMethodsMu.Unlock()
+ fn, ok := prog.objectMethods[obj]
+ if !ok {
+ fn = createFunction(prog, obj, obj.Name(), nil, nil, "")
+ fn.Synthetic = "from type information (on demand)"
+ fn.buildshared = b.shared()
+ b.enqueue(fn)
+
+ if prog.objectMethods == nil {
+ prog.objectMethods = make(map[*types.Func]*Function)
+ }
+ prog.objectMethods[obj] = fn
+ } else {
+ b.waitForSharedFunction(fn)
+ }
+ return fn
+}
+
+// LookupMethod returns the implementation of the method of type T
+// identified by (pkg, name). It returns nil if the method exists but
+// is an interface method or generic method, and panics if T has no such method.
+func (prog *Program) LookupMethod(T types.Type, pkg *types.Package, name string) *Function {
+ sel := prog.MethodSets.MethodSet(T).Lookup(pkg, name)
+ if sel == nil {
+ panic(fmt.Sprintf("%s has no method %s", T, types.Id(pkg, name)))
+ }
+ return prog.MethodValue(sel)
+}
+
+// methodSet contains the (concrete) methods of a concrete type (non-interface, non-parameterized).
+type methodSet struct {
+ mapping map[string]*Function // populated lazily
+}
+
+// RuntimeTypes returns a new unordered slice containing all types in
+// the program for which a runtime type is required.
+//
+// A runtime type is required for any non-parameterized, non-interface
+// type that is converted to an interface, or for any type (including
+// interface types) derivable from one through reflection.
+//
+// The methods of such types may be reachable through reflection or
+// interface calls even if they are never called directly.
+//
+// Thread-safe.
+//
+// Acquires prog.runtimeTypesMu.
+func (prog *Program) RuntimeTypes() []types.Type {
+ prog.runtimeTypesMu.Lock()
+ defer prog.runtimeTypesMu.Unlock()
+ return prog.runtimeTypes.Keys()
+}
+
+// forEachReachable calls f for type T and each type reachable from
+// its type through reflection.
+//
+// The function f must use memoization to break cycles and
+// return false when the type has already been visited.
+//
+// TODO(adonovan): publish in typeutil and share with go/callgraph/rta.
+func forEachReachable(msets *typeutil.MethodSetCache, T types.Type, f func(types.Type) bool) {
+ var visit func(T types.Type, skip bool)
+ visit = func(T types.Type, skip bool) {
+ if !skip {
+ if !f(T) {
+ return
+ }
+ }
+
+ // Recursion over signatures of each method.
+ tmset := msets.MethodSet(T)
+ for i := 0; i < tmset.Len(); i++ {
+ sig := tmset.At(i).Type().(*types.Signature)
+ // It is tempting to call visit(sig, false)
+ // but, as noted in golang.org/cl/65450043,
+ // the Signature.Recv field is ignored by
+ // types.Identical and typeutil.Map, which
+ // is confusing at best.
+ //
+ // More importantly, the true signature rtype
+ // reachable from a method using reflection
+ // has no receiver but an extra ordinary parameter.
+ // For the Read method of io.Reader we want:
+ // func(Reader, []byte) (int, error)
+ // but here sig is:
+ // func([]byte) (int, error)
+ // with .Recv = Reader (though it is hard to
+ // notice because it doesn't affect Signature.String
+ // or types.Identical).
+ //
+ // TODO(adonovan): construct and visit the correct
+ // non-method signature with an extra parameter
+ // (though since unnamed func types have no methods
+ // there is essentially no actual demand for this).
+ //
+ // TODO(adonovan): document whether or not it is
+ // safe to skip non-exported methods (as RTA does).
+ visit(sig.Params(), true) // skip the Tuple
+ visit(sig.Results(), true) // skip the Tuple
+ }
+
+ switch T := T.(type) {
+ case *aliases.Alias:
+ visit(aliases.Unalias(T), skip) // emulates the pre-Alias behavior
+
+ case *types.Basic:
+ // nop
+
+ case *types.Interface:
+ // nop---handled by recursion over method set.
+
+ case *types.Pointer:
+ visit(T.Elem(), false)
+
+ case *types.Slice:
+ visit(T.Elem(), false)
+
+ case *types.Chan:
+ visit(T.Elem(), false)
+
+ case *types.Map:
+ visit(T.Key(), false)
+ visit(T.Elem(), false)
+
+ case *types.Signature:
+ if T.Recv() != nil {
+ panic(fmt.Sprintf("Signature %s has Recv %s", T, T.Recv()))
+ }
+ visit(T.Params(), true) // skip the Tuple
+ visit(T.Results(), true) // skip the Tuple
+
+ case *types.Named:
+ // A pointer-to-named type can be derived from a named
+ // type via reflection. It may have methods too.
+ visit(types.NewPointer(T), false)
+
+ // Consider 'type T struct{S}' where S has methods.
+ // Reflection provides no way to get from T to struct{S},
+ // only to S, so the method set of struct{S} is unwanted,
+ // so set 'skip' flag during recursion.
+ visit(T.Underlying(), true) // skip the unnamed type
+
+ case *types.Array:
+ visit(T.Elem(), false)
+
+ case *types.Struct:
+ for i, n := 0, T.NumFields(); i < n; i++ {
+ // TODO(adonovan): document whether or not
+ // it is safe to skip non-exported fields.
+ visit(T.Field(i).Type(), false)
+ }
+
+ case *types.Tuple:
+ for i, n := 0, T.Len(); i < n; i++ {
+ visit(T.At(i).Type(), false)
+ }
+
+ case *types.TypeParam, *types.Union:
+ // forEachReachable must not be called on parameterized types.
+ panic(T)
+
+ default:
+ panic(T)
+ }
+ }
+ visit(T, false)
+}