...

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/internal/typeparams"
    14  )
    15  
    16  // MethodValue returns the Function implementing method sel, building
    17  // wrapper methods on demand.  It returns nil if sel denotes an
    18  // abstract (interface or parameterized) method.
    19  //
    20  // Precondition: sel.Kind() == MethodVal.
    21  //
    22  // Thread-safe.
    23  //
    24  // EXCLUSIVE_LOCKS_ACQUIRED(prog.methodsMu)
    25  func (prog *Program) MethodValue(sel *types.Selection) *Function {
    26  	if sel.Kind() != types.MethodVal {
    27  		panic(fmt.Sprintf("MethodValue(%s) kind != MethodVal", sel))
    28  	}
    29  	T := sel.Recv()
    30  	if types.IsInterface(T) {
    31  		return nil // abstract method (interface, possibly type param)
    32  	}
    33  	if prog.mode&LogSource != 0 {
    34  		defer logStack("MethodValue %s %v", T, sel)()
    35  	}
    36  
    37  	var m *Function
    38  	b := builder{created: &creator{}}
    39  
    40  	prog.methodsMu.Lock()
    41  	// Checks whether a type param is reachable from T.
    42  	// This is an expensive check. May need to be optimized later.
    43  	if !prog.parameterized.isParameterized(T) {
    44  		m = prog.addMethod(prog.createMethodSet(T), sel, b.created)
    45  	}
    46  	prog.methodsMu.Unlock()
    47  
    48  	if m == nil {
    49  		return nil // abstract method (generic)
    50  	}
    51  	for !b.done() {
    52  		b.buildCreated()
    53  		b.needsRuntimeTypes()
    54  	}
    55  	return m
    56  }
    57  
    58  // LookupMethod returns the implementation of the method of type T
    59  // identified by (pkg, name).  It returns nil if the method exists but
    60  // is abstract, and panics if T has no such method.
    61  func (prog *Program) LookupMethod(T types.Type, pkg *types.Package, name string) *Function {
    62  	sel := prog.MethodSets.MethodSet(T).Lookup(pkg, name)
    63  	if sel == nil {
    64  		panic(fmt.Sprintf("%s has no method %s", T, types.Id(pkg, name)))
    65  	}
    66  	return prog.MethodValue(sel)
    67  }
    68  
    69  // methodSet contains the (concrete) methods of a concrete type (non-interface, non-parameterized).
    70  type methodSet struct {
    71  	mapping  map[string]*Function // populated lazily
    72  	complete bool                 // mapping contains all methods
    73  }
    74  
    75  // Precondition: T is a concrete type, e.g. !isInterface(T) and not parameterized.
    76  // EXCLUSIVE_LOCKS_REQUIRED(prog.methodsMu)
    77  func (prog *Program) createMethodSet(T types.Type) *methodSet {
    78  	if prog.mode&SanityCheckFunctions != 0 {
    79  		if types.IsInterface(T) || prog.parameterized.isParameterized(T) {
    80  			panic("type is interface or parameterized")
    81  		}
    82  	}
    83  	mset, ok := prog.methodSets.At(T).(*methodSet)
    84  	if !ok {
    85  		mset = &methodSet{mapping: make(map[string]*Function)}
    86  		prog.methodSets.Set(T, mset)
    87  	}
    88  	return mset
    89  }
    90  
    91  // Adds any created functions to cr.
    92  // Precondition: T is a concrete type, e.g. !isInterface(T) and not parameterized.
    93  // EXCLUSIVE_LOCKS_REQUIRED(prog.methodsMu)
    94  func (prog *Program) addMethod(mset *methodSet, sel *types.Selection, cr *creator) *Function {
    95  	if sel.Kind() == types.MethodExpr {
    96  		panic(sel)
    97  	}
    98  	id := sel.Obj().Id()
    99  	fn := mset.mapping[id]
   100  	if fn == nil {
   101  		sel := toSelection(sel)
   102  		obj := sel.obj.(*types.Func)
   103  
   104  		needsPromotion := len(sel.index) > 1
   105  		needsIndirection := !isPointer(recvType(obj)) && isPointer(sel.recv)
   106  		if needsPromotion || needsIndirection {
   107  			fn = makeWrapper(prog, sel, cr)
   108  		} else {
   109  			fn = prog.originFunc(obj)
   110  			if fn.typeparams.Len() > 0 { // instantiate
   111  				targs := receiverTypeArgs(obj)
   112  				fn = prog.lookupOrCreateInstance(fn, targs, cr)
   113  			}
   114  		}
   115  		if fn.Signature.Recv() == nil {
   116  			panic(fn) // missing receiver
   117  		}
   118  		mset.mapping[id] = fn
   119  	}
   120  	return fn
   121  }
   122  
   123  // RuntimeTypes returns a new unordered slice containing all
   124  // concrete types in the program for which a complete (non-empty)
   125  // method set is required at run-time.
   126  //
   127  // Thread-safe.
   128  //
   129  // EXCLUSIVE_LOCKS_ACQUIRED(prog.methodsMu)
   130  func (prog *Program) RuntimeTypes() []types.Type {
   131  	prog.methodsMu.Lock()
   132  	defer prog.methodsMu.Unlock()
   133  
   134  	var res []types.Type
   135  	prog.methodSets.Iterate(func(T types.Type, v interface{}) {
   136  		if v.(*methodSet).complete {
   137  			res = append(res, T)
   138  		}
   139  	})
   140  	return res
   141  }
   142  
   143  // declaredFunc returns the concrete function/method denoted by obj.
   144  // Panic ensues if there is none.
   145  func (prog *Program) declaredFunc(obj *types.Func) *Function {
   146  	if v := prog.packageLevelMember(obj); v != nil {
   147  		return v.(*Function)
   148  	}
   149  	panic("no concrete method: " + obj.String())
   150  }
   151  
   152  // needMethodsOf ensures that runtime type information (including the
   153  // complete method set) is available for the specified type T and all
   154  // its subcomponents.
   155  //
   156  // needMethodsOf must be called for at least every type that is an
   157  // operand of some MakeInterface instruction, and for the type of
   158  // every exported package member.
   159  //
   160  // Adds any created functions to cr.
   161  //
   162  // Precondition: T is not a method signature (*Signature with Recv()!=nil).
   163  // Precondition: T is not parameterized.
   164  //
   165  // Thread-safe.  (Called via Package.build from multiple builder goroutines.)
   166  //
   167  // TODO(adonovan): make this faster.  It accounts for 20% of SSA build time.
   168  //
   169  // EXCLUSIVE_LOCKS_ACQUIRED(prog.methodsMu)
   170  func (prog *Program) needMethodsOf(T types.Type, cr *creator) {
   171  	prog.methodsMu.Lock()
   172  	prog.needMethods(T, false, cr)
   173  	prog.methodsMu.Unlock()
   174  }
   175  
   176  // Precondition: T is not a method signature (*Signature with Recv()!=nil).
   177  // Precondition: T is not parameterized.
   178  // Recursive case: skip => don't create methods for T.
   179  //
   180  // EXCLUSIVE_LOCKS_REQUIRED(prog.methodsMu)
   181  func (prog *Program) needMethods(T types.Type, skip bool, cr *creator) {
   182  	// Each package maintains its own set of types it has visited.
   183  	if prevSkip, ok := prog.runtimeTypes.At(T).(bool); ok {
   184  		// needMethods(T) was previously called
   185  		if !prevSkip || skip {
   186  			return // already seen, with same or false 'skip' value
   187  		}
   188  	}
   189  	prog.runtimeTypes.Set(T, skip)
   190  
   191  	tmset := prog.MethodSets.MethodSet(T)
   192  
   193  	if !skip && !types.IsInterface(T) && tmset.Len() > 0 {
   194  		// Create methods of T.
   195  		mset := prog.createMethodSet(T)
   196  		if !mset.complete {
   197  			mset.complete = true
   198  			n := tmset.Len()
   199  			for i := 0; i < n; i++ {
   200  				prog.addMethod(mset, tmset.At(i), cr)
   201  			}
   202  		}
   203  	}
   204  
   205  	// Recursion over signatures of each method.
   206  	for i := 0; i < tmset.Len(); i++ {
   207  		sig := tmset.At(i).Type().(*types.Signature)
   208  		prog.needMethods(sig.Params(), false, cr)
   209  		prog.needMethods(sig.Results(), false, cr)
   210  	}
   211  
   212  	switch t := T.(type) {
   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  		prog.needMethods(t.Elem(), false, cr)
   221  
   222  	case *types.Slice:
   223  		prog.needMethods(t.Elem(), false, cr)
   224  
   225  	case *types.Chan:
   226  		prog.needMethods(t.Elem(), false, cr)
   227  
   228  	case *types.Map:
   229  		prog.needMethods(t.Key(), false, cr)
   230  		prog.needMethods(t.Elem(), false, cr)
   231  
   232  	case *types.Signature:
   233  		if t.Recv() != nil {
   234  			panic(fmt.Sprintf("Signature %s has Recv %s", t, t.Recv()))
   235  		}
   236  		prog.needMethods(t.Params(), false, cr)
   237  		prog.needMethods(t.Results(), false, cr)
   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  		prog.needMethods(types.NewPointer(T), false, cr)
   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  		prog.needMethods(t.Underlying(), true, cr)
   249  
   250  	case *types.Array:
   251  		prog.needMethods(t.Elem(), false, cr)
   252  
   253  	case *types.Struct:
   254  		for i, n := 0, t.NumFields(); i < n; i++ {
   255  			prog.needMethods(t.Field(i).Type(), false, cr)
   256  		}
   257  
   258  	case *types.Tuple:
   259  		for i, n := 0, t.Len(); i < n; i++ {
   260  			prog.needMethods(t.At(i).Type(), false, cr)
   261  		}
   262  
   263  	case *typeparams.TypeParam:
   264  		panic(T) // type parameters are always abstract.
   265  
   266  	case *typeparams.Union:
   267  		// nop
   268  
   269  	default:
   270  		panic(T)
   271  	}
   272  }
   273  

View as plain text