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