| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240 |
- // Copyright 2013 The Go Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style
- // license that can be found in the LICENSE file.
- package ssa
- // This file defines utilities for population of method sets.
- import (
- "fmt"
- "go/types"
- )
- // MethodValue returns the Function implementing method sel, building
- // wrapper methods on demand. It returns nil if sel denotes an
- // abstract (interface) method.
- //
- // Precondition: sel.Kind() == MethodVal.
- //
- // Thread-safe.
- //
- // EXCLUSIVE_LOCKS_ACQUIRED(prog.methodsMu)
- //
- func (prog *Program) MethodValue(sel *types.Selection) *Function {
- if sel.Kind() != types.MethodVal {
- panic(fmt.Sprintf("MethodValue(%s) kind != MethodVal", sel))
- }
- T := sel.Recv()
- if isInterface(T) {
- return nil // abstract method
- }
- if prog.mode&LogSource != 0 {
- defer logStack("MethodValue %s %v", T, sel)()
- }
- prog.methodsMu.Lock()
- defer prog.methodsMu.Unlock()
- return prog.addMethod(prog.createMethodSet(T), sel)
- }
- // LookupMethod returns the implementation of the method of type T
- // identified by (pkg, name). It returns nil if the method exists but
- // is abstract, and panics if T has no such method.
- //
- func (prog *Program) LookupMethod(T types.Type, pkg *types.Package, name string) *Function {
- sel := prog.MethodSets.MethodSet(T).Lookup(pkg, name)
- if sel == nil {
- panic(fmt.Sprintf("%s has no method %s", T, types.Id(pkg, name)))
- }
- return prog.MethodValue(sel)
- }
- // methodSet contains the (concrete) methods of a non-interface type.
- type methodSet struct {
- mapping map[string]*Function // populated lazily
- complete bool // mapping contains all methods
- }
- // Precondition: !isInterface(T).
- // EXCLUSIVE_LOCKS_REQUIRED(prog.methodsMu)
- func (prog *Program) createMethodSet(T types.Type) *methodSet {
- mset, ok := prog.methodSets.At(T).(*methodSet)
- if !ok {
- mset = &methodSet{mapping: make(map[string]*Function)}
- prog.methodSets.Set(T, mset)
- }
- return mset
- }
- // EXCLUSIVE_LOCKS_REQUIRED(prog.methodsMu)
- func (prog *Program) addMethod(mset *methodSet, sel *types.Selection) *Function {
- if sel.Kind() == types.MethodExpr {
- panic(sel)
- }
- id := sel.Obj().Id()
- fn := mset.mapping[id]
- if fn == nil {
- obj := sel.Obj().(*types.Func)
- needsPromotion := len(sel.Index()) > 1
- needsIndirection := !isPointer(recvType(obj)) && isPointer(sel.Recv())
- if needsPromotion || needsIndirection {
- fn = makeWrapper(prog, sel)
- } else {
- fn = prog.declaredFunc(obj)
- }
- if fn.Signature.Recv() == nil {
- panic(fn) // missing receiver
- }
- mset.mapping[id] = fn
- }
- return fn
- }
- // RuntimeTypes returns a new unordered slice containing all
- // concrete types in the program for which a complete (non-empty)
- // method set is required at run-time.
- //
- // Thread-safe.
- //
- // EXCLUSIVE_LOCKS_ACQUIRED(prog.methodsMu)
- //
- func (prog *Program) RuntimeTypes() []types.Type {
- prog.methodsMu.Lock()
- defer prog.methodsMu.Unlock()
- var res []types.Type
- prog.methodSets.Iterate(func(T types.Type, v interface{}) {
- if v.(*methodSet).complete {
- res = append(res, T)
- }
- })
- return res
- }
- // declaredFunc returns the concrete function/method denoted by obj.
- // Panic ensues if there is none.
- //
- func (prog *Program) declaredFunc(obj *types.Func) *Function {
- if v := prog.packageLevelValue(obj); v != nil {
- return v.(*Function)
- }
- panic("no concrete method: " + obj.String())
- }
- // needMethodsOf ensures that runtime type information (including the
- // complete method set) is available for the specified type T and all
- // its subcomponents.
- //
- // needMethodsOf must be called for at least every type that is an
- // operand of some MakeInterface instruction, and for the type of
- // every exported package member.
- //
- // Precondition: T is not a method signature (*Signature with Recv()!=nil).
- //
- // Thread-safe. (Called via emitConv from multiple builder goroutines.)
- //
- // TODO(adonovan): make this faster. It accounts for 20% of SSA build time.
- //
- // EXCLUSIVE_LOCKS_ACQUIRED(prog.methodsMu)
- //
- func (prog *Program) needMethodsOf(T types.Type) {
- prog.methodsMu.Lock()
- prog.needMethods(T, false)
- prog.methodsMu.Unlock()
- }
- // Precondition: T is not a method signature (*Signature with Recv()!=nil).
- // Recursive case: skip => don't create methods for T.
- //
- // EXCLUSIVE_LOCKS_REQUIRED(prog.methodsMu)
- //
- func (prog *Program) needMethods(T types.Type, skip bool) {
- // Each package maintains its own set of types it has visited.
- if prevSkip, ok := prog.runtimeTypes.At(T).(bool); ok {
- // needMethods(T) was previously called
- if !prevSkip || skip {
- return // already seen, with same or false 'skip' value
- }
- }
- prog.runtimeTypes.Set(T, skip)
- tmset := prog.MethodSets.MethodSet(T)
- if !skip && !isInterface(T) && tmset.Len() > 0 {
- // Create methods of T.
- mset := prog.createMethodSet(T)
- if !mset.complete {
- mset.complete = true
- n := tmset.Len()
- for i := 0; i < n; i++ {
- prog.addMethod(mset, tmset.At(i))
- }
- }
- }
- // Recursion over signatures of each method.
- for i := 0; i < tmset.Len(); i++ {
- sig := tmset.At(i).Type().(*types.Signature)
- prog.needMethods(sig.Params(), false)
- prog.needMethods(sig.Results(), false)
- }
- switch t := T.(type) {
- case *types.Basic:
- // nop
- case *types.Interface:
- // nop---handled by recursion over method set.
- case *types.Pointer:
- prog.needMethods(t.Elem(), false)
- case *types.Slice:
- prog.needMethods(t.Elem(), false)
- case *types.Chan:
- prog.needMethods(t.Elem(), false)
- case *types.Map:
- prog.needMethods(t.Key(), false)
- prog.needMethods(t.Elem(), false)
- case *types.Signature:
- if t.Recv() != nil {
- panic(fmt.Sprintf("Signature %s has Recv %s", t, t.Recv()))
- }
- prog.needMethods(t.Params(), false)
- prog.needMethods(t.Results(), false)
- case *types.Named:
- // A pointer-to-named type can be derived from a named
- // type via reflection. It may have methods too.
- prog.needMethods(types.NewPointer(T), false)
- // Consider 'type T struct{S}' where S has methods.
- // Reflection provides no way to get from T to struct{S},
- // only to S, so the method set of struct{S} is unwanted,
- // so set 'skip' flag during recursion.
- prog.needMethods(t.Underlying(), true)
- case *types.Array:
- prog.needMethods(t.Elem(), false)
- case *types.Struct:
- for i, n := 0, t.NumFields(); i < n; i++ {
- prog.needMethods(t.Field(i).Type(), false)
- }
- case *types.Tuple:
- for i, n := 0, t.Len(); i < n; i++ {
- prog.needMethods(t.At(i).Type(), false)
- }
- default:
- panic(T)
- }
- }
|