github.com/powerman/golang-tools@v0.1.11-0.20220410185822-5ad214d8d803/go/ssa/methods.go (about)

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