...

Source file src/golang.org/x/tools/go/ssa/methods.go

Documentation: golang.org/x/tools/go/ssa

     1  // Copyright 2013 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package ssa
     6  
     7  // This file defines utilities for population of method sets.
     8  
     9  import (
    10  	"fmt"
    11  	"go/types"
    12  
    13  	"golang.org/x/tools/go/types/typeutil"
    14  	"golang.org/x/tools/internal/aliases"
    15  )
    16  
    17  // MethodValue returns the Function implementing method sel, building
    18  // wrapper methods on demand. It returns nil if sel denotes an
    19  // interface or generic method.
    20  //
    21  // Precondition: sel.Kind() == MethodVal.
    22  //
    23  // Thread-safe.
    24  //
    25  // Acquires prog.methodsMu.
    26  func (prog *Program) MethodValue(sel *types.Selection) *Function {
    27  	if sel.Kind() != types.MethodVal {
    28  		panic(fmt.Sprintf("MethodValue(%s) kind != MethodVal", sel))
    29  	}
    30  	T := sel.Recv()
    31  	if types.IsInterface(T) {
    32  		return nil // interface method or type parameter
    33  	}
    34  
    35  	if prog.isParameterized(T) {
    36  		return nil // generic method
    37  	}
    38  
    39  	if prog.mode&LogSource != 0 {
    40  		defer logStack("MethodValue %s %v", T, sel)()
    41  	}
    42  
    43  	var cr creator
    44  
    45  	m := func() *Function {
    46  		prog.methodsMu.Lock()
    47  		defer prog.methodsMu.Unlock()
    48  
    49  		// Get or create SSA method set.
    50  		mset, ok := prog.methodSets.At(T).(*methodSet)
    51  		if !ok {
    52  			mset = &methodSet{mapping: make(map[string]*Function)}
    53  			prog.methodSets.Set(T, mset)
    54  		}
    55  
    56  		// Get or create SSA method.
    57  		id := sel.Obj().Id()
    58  		fn, ok := mset.mapping[id]
    59  		if !ok {
    60  			obj := sel.Obj().(*types.Func)
    61  			needsPromotion := len(sel.Index()) > 1
    62  			needsIndirection := !isPointer(recvType(obj)) && isPointer(T)
    63  			if needsPromotion || needsIndirection {
    64  				fn = createWrapper(prog, toSelection(sel), &cr)
    65  			} else {
    66  				fn = prog.objectMethod(obj, &cr)
    67  			}
    68  			if fn.Signature.Recv() == nil {
    69  				panic(fn)
    70  			}
    71  			mset.mapping[id] = fn
    72  		}
    73  
    74  		return fn
    75  	}()
    76  
    77  	b := builder{created: &cr}
    78  	b.iterate()
    79  
    80  	return m
    81  }
    82  
    83  // objectMethod returns the Function for a given method symbol.
    84  // The symbol may be an instance of a generic function. It need not
    85  // belong to an existing SSA package created by a call to
    86  // prog.CreatePackage.
    87  //
    88  // objectMethod panics if the function is not a method.
    89  //
    90  // Acquires prog.objectMethodsMu.
    91  func (prog *Program) objectMethod(obj *types.Func, cr *creator) *Function {
    92  	sig := obj.Type().(*types.Signature)
    93  	if sig.Recv() == nil {
    94  		panic("not a method: " + obj.String())
    95  	}
    96  
    97  	// Belongs to a created package?
    98  	if fn := prog.FuncValue(obj); fn != nil {
    99  		return fn
   100  	}
   101  
   102  	// Instantiation of generic?
   103  	if originObj := obj.Origin(); originObj != obj {
   104  		origin := prog.objectMethod(originObj, cr)
   105  		assert(origin.typeparams.Len() > 0, "origin is not generic")
   106  		targs := receiverTypeArgs(obj)
   107  		return origin.instance(targs, cr)
   108  	}
   109  
   110  	// Consult/update cache of methods created from types.Func.
   111  	prog.objectMethodsMu.Lock()
   112  	defer prog.objectMethodsMu.Unlock()
   113  	fn, ok := prog.objectMethods[obj]
   114  	if !ok {
   115  		fn = createFunction(prog, obj, obj.Name(), nil, nil, "", cr)
   116  		fn.Synthetic = "from type information (on demand)"
   117  
   118  		if prog.objectMethods == nil {
   119  			prog.objectMethods = make(map[*types.Func]*Function)
   120  		}
   121  		prog.objectMethods[obj] = fn
   122  	}
   123  	return fn
   124  }
   125  
   126  // LookupMethod returns the implementation of the method of type T
   127  // identified by (pkg, name).  It returns nil if the method exists but
   128  // is an interface method or generic method, and panics if T has no such method.
   129  func (prog *Program) LookupMethod(T types.Type, pkg *types.Package, name string) *Function {
   130  	sel := prog.MethodSets.MethodSet(T).Lookup(pkg, name)
   131  	if sel == nil {
   132  		panic(fmt.Sprintf("%s has no method %s", T, types.Id(pkg, name)))
   133  	}
   134  	return prog.MethodValue(sel)
   135  }
   136  
   137  // methodSet contains the (concrete) methods of a concrete type (non-interface, non-parameterized).
   138  type methodSet struct {
   139  	mapping map[string]*Function // populated lazily
   140  }
   141  
   142  // RuntimeTypes returns a new unordered slice containing all types in
   143  // the program for which a runtime type is required.
   144  //
   145  // A runtime type is required for any non-parameterized, non-interface
   146  // type that is converted to an interface, or for any type (including
   147  // interface types) derivable from one through reflection.
   148  //
   149  // The methods of such types may be reachable through reflection or
   150  // interface calls even if they are never called directly.
   151  //
   152  // Thread-safe.
   153  //
   154  // Acquires prog.runtimeTypesMu.
   155  func (prog *Program) RuntimeTypes() []types.Type {
   156  	prog.runtimeTypesMu.Lock()
   157  	defer prog.runtimeTypesMu.Unlock()
   158  	return prog.runtimeTypes.Keys()
   159  }
   160  
   161  // forEachReachable calls f for type T and each type reachable from
   162  // its type through reflection.
   163  //
   164  // The function f must use memoization to break cycles and
   165  // return false when the type has already been visited.
   166  //
   167  // TODO(adonovan): publish in typeutil and share with go/callgraph/rta.
   168  func forEachReachable(msets *typeutil.MethodSetCache, T types.Type, f func(types.Type) bool) {
   169  	var visit func(T types.Type, skip bool)
   170  	visit = func(T types.Type, skip bool) {
   171  		if !skip {
   172  			if !f(T) {
   173  				return
   174  			}
   175  		}
   176  
   177  		// Recursion over signatures of each method.
   178  		tmset := msets.MethodSet(T)
   179  		for i := 0; i < tmset.Len(); i++ {
   180  			sig := tmset.At(i).Type().(*types.Signature)
   181  			// It is tempting to call visit(sig, false)
   182  			// but, as noted in golang.org/cl/65450043,
   183  			// the Signature.Recv field is ignored by
   184  			// types.Identical and typeutil.Map, which
   185  			// is confusing at best.
   186  			//
   187  			// More importantly, the true signature rtype
   188  			// reachable from a method using reflection
   189  			// has no receiver but an extra ordinary parameter.
   190  			// For the Read method of io.Reader we want:
   191  			//   func(Reader, []byte) (int, error)
   192  			// but here sig is:
   193  			//   func([]byte) (int, error)
   194  			// with .Recv = Reader (though it is hard to
   195  			// notice because it doesn't affect Signature.String
   196  			// or types.Identical).
   197  			//
   198  			// TODO(adonovan): construct and visit the correct
   199  			// non-method signature with an extra parameter
   200  			// (though since unnamed func types have no methods
   201  			// there is essentially no actual demand for this).
   202  			//
   203  			// TODO(adonovan): document whether or not it is
   204  			// safe to skip non-exported methods (as RTA does).
   205  			visit(sig.Params(), true)  // skip the Tuple
   206  			visit(sig.Results(), true) // skip the Tuple
   207  		}
   208  
   209  		switch T := T.(type) {
   210  		case *aliases.Alias:
   211  			visit(aliases.Unalias(T), skip) // emulates the pre-Alias behavior
   212  
   213  		case *types.Basic:
   214  			// nop
   215  
   216  		case *types.Interface:
   217  			// nop---handled by recursion over method set.
   218  
   219  		case *types.Pointer:
   220  			visit(T.Elem(), false)
   221  
   222  		case *types.Slice:
   223  			visit(T.Elem(), false)
   224  
   225  		case *types.Chan:
   226  			visit(T.Elem(), false)
   227  
   228  		case *types.Map:
   229  			visit(T.Key(), false)
   230  			visit(T.Elem(), false)
   231  
   232  		case *types.Signature:
   233  			if T.Recv() != nil {
   234  				panic(fmt.Sprintf("Signature %s has Recv %s", T, T.Recv()))
   235  			}
   236  			visit(T.Params(), true)  // skip the Tuple
   237  			visit(T.Results(), true) // skip the Tuple
   238  
   239  		case *types.Named:
   240  			// A pointer-to-named type can be derived from a named
   241  			// type via reflection.  It may have methods too.
   242  			visit(types.NewPointer(T), false)
   243  
   244  			// Consider 'type T struct{S}' where S has methods.
   245  			// Reflection provides no way to get from T to struct{S},
   246  			// only to S, so the method set of struct{S} is unwanted,
   247  			// so set 'skip' flag during recursion.
   248  			visit(T.Underlying(), true) // skip the unnamed type
   249  
   250  		case *types.Array:
   251  			visit(T.Elem(), false)
   252  
   253  		case *types.Struct:
   254  			for i, n := 0, T.NumFields(); i < n; i++ {
   255  				// TODO(adonovan): document whether or not
   256  				// it is safe to skip non-exported fields.
   257  				visit(T.Field(i).Type(), false)
   258  			}
   259  
   260  		case *types.Tuple:
   261  			for i, n := 0, T.Len(); i < n; i++ {
   262  				visit(T.At(i).Type(), false)
   263  			}
   264  
   265  		case *types.TypeParam, *types.Union:
   266  			// forEachReachable must not be called on parameterized types.
   267  			panic(T)
   268  
   269  		default:
   270  			panic(T)
   271  		}
   272  	}
   273  	visit(T, false)
   274  }
   275  

View as plain text