// 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" "golang.org/x/tools/go/types" ) // Method 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. // // TODO(adonovan): rename this to MethodValue because of the // precondition, and for consistency with functions in source.go. // // Thread-safe. // // EXCLUSIVE_LOCKS_ACQUIRED(prog.methodsMu) // func (prog *Program) Method(sel *types.Selection) *Function { if sel.Kind() != types.MethodVal { panic(fmt.Sprintf("Method(%s) kind != MethodVal", sel)) } T := sel.Recv() if isInterface(T) { return nil // abstract method } if prog.mode&LogSource != 0 { defer logStack("Method %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.Method(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) } }