methods.go raw

   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/typesinternal"
  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 b builder
  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))
  65  				fn.buildshared = b.shared()
  66  				b.enqueue(fn)
  67  			} else {
  68  				fn = prog.objectMethod(obj, &b)
  69  			}
  70  			if fn.Signature.Recv() == nil {
  71  				panic(fn)
  72  			}
  73  			mset.mapping[id] = fn
  74  		} else {
  75  			b.waitForSharedFunction(fn)
  76  		}
  77  
  78  		return fn
  79  	}()
  80  
  81  	b.iterate()
  82  
  83  	return m
  84  }
  85  
  86  // objectMethod returns the Function for a given method symbol.
  87  // The symbol may be an instance of a generic function. It need not
  88  // belong to an existing SSA package created by a call to
  89  // prog.CreatePackage.
  90  //
  91  // objectMethod panics if the function is not a method.
  92  //
  93  // Acquires prog.objectMethodsMu.
  94  func (prog *Program) objectMethod(obj *types.Func, b *builder) *Function {
  95  	sig := obj.Type().(*types.Signature)
  96  	if sig.Recv() == nil {
  97  		panic("not a method: " + obj.String())
  98  	}
  99  
 100  	// Belongs to a created package?
 101  	if fn := prog.FuncValue(obj); fn != nil {
 102  		return fn
 103  	}
 104  
 105  	// Instantiation of generic?
 106  	if originObj := obj.Origin(); originObj != obj {
 107  		origin := prog.objectMethod(originObj, b)
 108  		assert(origin.typeparams.Len() > 0, "origin is not generic")
 109  		targs := receiverTypeArgs(obj)
 110  		return origin.instance(targs, b)
 111  	}
 112  
 113  	// Consult/update cache of methods created from types.Func.
 114  	prog.objectMethodsMu.Lock()
 115  	defer prog.objectMethodsMu.Unlock()
 116  	fn, ok := prog.objectMethods[obj]
 117  	if !ok {
 118  		fn = createFunction(prog, obj, obj.Name(), nil, nil, "")
 119  		fn.Synthetic = "from type information (on demand)"
 120  		fn.buildshared = b.shared()
 121  		b.enqueue(fn)
 122  
 123  		if prog.objectMethods == nil {
 124  			prog.objectMethods = make(map[*types.Func]*Function)
 125  		}
 126  		prog.objectMethods[obj] = fn
 127  	} else {
 128  		b.waitForSharedFunction(fn)
 129  	}
 130  	return fn
 131  }
 132  
 133  // LookupMethod returns the implementation of the method of type T
 134  // identified by (pkg, name).  It returns nil if the method exists but
 135  // is an interface method or generic method, and panics if T has no such method.
 136  func (prog *Program) LookupMethod(T types.Type, pkg *types.Package, name string) *Function {
 137  	sel := prog.MethodSets.MethodSet(T).Lookup(pkg, name)
 138  	if sel == nil {
 139  		panic(fmt.Sprintf("%s has no method %s", T, types.Id(pkg, name)))
 140  	}
 141  	return prog.MethodValue(sel)
 142  }
 143  
 144  // methodSet contains the (concrete) methods of a concrete type (non-interface, non-parameterized).
 145  type methodSet struct {
 146  	mapping map[string]*Function // populated lazily
 147  }
 148  
 149  // RuntimeTypes returns a new unordered slice containing all types in
 150  // the program for which a runtime type is required.
 151  //
 152  // A runtime type is required for any non-parameterized, non-interface
 153  // type that is converted to an interface, or for any type (including
 154  // interface types) derivable from one through reflection.
 155  //
 156  // The methods of such types may be reachable through reflection or
 157  // interface calls even if they are never called directly.
 158  //
 159  // Thread-safe.
 160  //
 161  // Acquires prog.makeInterfaceTypesMu.
 162  func (prog *Program) RuntimeTypes() []types.Type {
 163  	prog.makeInterfaceTypesMu.Lock()
 164  	defer prog.makeInterfaceTypesMu.Unlock()
 165  
 166  	// Compute the derived types on demand, since many SSA clients
 167  	// never call RuntimeTypes, and those that do typically call
 168  	// it once (often within ssautil.AllFunctions, which will
 169  	// eventually not use it; see Go issue #69291.) This
 170  	// eliminates the need to eagerly compute all the element
 171  	// types during SSA building.
 172  	var runtimeTypes []types.Type
 173  	add := func(t types.Type) { runtimeTypes = append(runtimeTypes, t) }
 174  	var set typeutil.Map // for de-duping identical types
 175  	for t := range prog.makeInterfaceTypes {
 176  		typesinternal.ForEachElement(&set, &prog.MethodSets, t, add)
 177  	}
 178  
 179  	return runtimeTypes
 180  }
 181