go.tools/go/ssa: rename promote.go and split into two files, {methods,wrappers}.go.
Also: - move shared utilities to util.go. - rename types.Selection vars. LGTM=gri R=gri CC=golang-codereviews https://golang.org/cl/113550043
This commit is contained in:
parent
3442faf3c1
commit
31c2077fe3
|
@ -0,0 +1,196 @@
|
|||
// 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"
|
||||
|
||||
"code.google.com/p/go.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)
|
||||
}
|
||||
|
||||
// makeMethods ensures that all concrete methods of type T are
|
||||
// generated. It is equivalent to calling prog.Method() on all
|
||||
// members of T.methodSet(), but acquires fewer locks.
|
||||
//
|
||||
// It reports whether the type's (concrete) method set is non-empty.
|
||||
//
|
||||
// Thread-safe.
|
||||
//
|
||||
// EXCLUSIVE_LOCKS_ACQUIRED(prog.methodsMu)
|
||||
//
|
||||
func (prog *Program) makeMethods(T types.Type) bool {
|
||||
if isInterface(T) {
|
||||
return false // abstract method
|
||||
}
|
||||
tmset := prog.MethodSets.MethodSet(T)
|
||||
n := tmset.Len()
|
||||
if n == 0 {
|
||||
return false // empty (common case)
|
||||
}
|
||||
|
||||
if prog.mode&LogSource != 0 {
|
||||
defer logStack("makeMethods %s", T)()
|
||||
}
|
||||
|
||||
prog.methodsMu.Lock()
|
||||
defer prog.methodsMu.Unlock()
|
||||
|
||||
mset := prog.createMethodSet(T)
|
||||
if !mset.complete {
|
||||
mset.complete = true
|
||||
for i := 0; i < n; i++ {
|
||||
prog.addMethod(mset, tmset.At(i))
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// TypesWithMethodSets 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.
|
||||
//
|
||||
// It is the union of pkg.TypesWithMethodSets() for all pkg in
|
||||
// prog.AllPackages().
|
||||
//
|
||||
// Thread-safe.
|
||||
//
|
||||
// EXCLUSIVE_LOCKS_ACQUIRED(prog.methodsMu)
|
||||
//
|
||||
func (prog *Program) TypesWithMethodSets() []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
|
||||
}
|
||||
|
||||
// TypesWithMethodSets returns an unordered slice containing the set
|
||||
// of all concrete types referenced within package pkg and not
|
||||
// belonging to some other package, for which a complete (non-empty)
|
||||
// method set is required at run-time.
|
||||
//
|
||||
// A type belongs to a package if it is a named type or a pointer to a
|
||||
// named type, and the name was defined in that package. All other
|
||||
// types belong to no package.
|
||||
//
|
||||
// A type may appear in the TypesWithMethodSets() set of multiple
|
||||
// distinct packages if that type belongs to no package. Typical
|
||||
// compilers emit method sets for such types multiple times (using
|
||||
// weak symbols) into each package that references them, with the
|
||||
// linker performing duplicate elimination.
|
||||
//
|
||||
// This set includes the types of all operands of some MakeInterface
|
||||
// instruction, the types of all exported members of some package, and
|
||||
// all types that are subcomponents, since even types that aren't used
|
||||
// directly may be derived via reflection.
|
||||
//
|
||||
// Callers must not mutate the result.
|
||||
//
|
||||
func (pkg *Package) TypesWithMethodSets() []types.Type {
|
||||
return pkg.methodSets
|
||||
}
|
||||
|
||||
// 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())
|
||||
}
|
|
@ -50,6 +50,12 @@ func isPointer(typ types.Type) bool {
|
|||
return ok
|
||||
}
|
||||
|
||||
// isInterface reports whether T's underlying type is an interface.
|
||||
func isInterface(T types.Type) bool {
|
||||
_, ok := T.Underlying().(*types.Interface)
|
||||
return ok
|
||||
}
|
||||
|
||||
// deref returns a pointer's element type; otherwise it returns typ.
|
||||
func deref(typ types.Type) types.Type {
|
||||
if p, ok := typ.Underlying().(*types.Pointer); ok {
|
||||
|
@ -58,6 +64,11 @@ func deref(typ types.Type) types.Type {
|
|||
return typ
|
||||
}
|
||||
|
||||
// recvType returns the receiver type of method obj.
|
||||
func recvType(obj *types.Func) types.Type {
|
||||
return obj.Type().(*types.Signature).Recv().Type()
|
||||
}
|
||||
|
||||
// DefaultType returns the default "typed" type for an "untyped" type;
|
||||
// it returns the incoming type for all other types. The default type
|
||||
// for untyped nil is untyped nil.
|
||||
|
|
|
@ -4,9 +4,8 @@
|
|||
|
||||
package ssa
|
||||
|
||||
// This file defines utilities for population of method sets and
|
||||
// synthesis of Functions that delegate to declared methods, which
|
||||
// come in three kinds:
|
||||
// This file defines synthesis of Functions that delegate to declared
|
||||
// methods, which come in three kinds:
|
||||
//
|
||||
// (1) wrappers: methods that wrap declared methods, performing
|
||||
// implicit pointer indirections and embedded field selections.
|
||||
|
@ -19,9 +18,6 @@ package ssa
|
|||
// free variable, supplied by a closure, is used as the receiver
|
||||
// for the method call. No indirections or field selections are
|
||||
// performed since they can be done before the call.
|
||||
//
|
||||
// TODO(adonovan): split and rename to {methodset,delegate}.go.
|
||||
// TODO(adonovan): use 'sel' not 'meth' for *types.Selection; reserve 'meth' for *Function.
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
@ -29,190 +25,7 @@ import (
|
|||
"code.google.com/p/go.tools/go/types"
|
||||
)
|
||||
|
||||
// Method returns the Function implementing method meth, building
|
||||
// wrapper methods on demand. It returns nil if meth denotes an
|
||||
// abstract (interface) method.
|
||||
//
|
||||
// Precondition: meth 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(meth *types.Selection) *Function {
|
||||
if meth.Kind() != types.MethodVal {
|
||||
panic(fmt.Sprintf("Method(%s) kind != MethodVal", meth))
|
||||
}
|
||||
T := meth.Recv()
|
||||
if isInterface(T) {
|
||||
return nil // abstract method
|
||||
}
|
||||
if prog.mode&LogSource != 0 {
|
||||
defer logStack("Method %s %v", T, meth)()
|
||||
}
|
||||
|
||||
prog.methodsMu.Lock()
|
||||
defer prog.methodsMu.Unlock()
|
||||
|
||||
return prog.addMethod(prog.createMethodSet(T), meth)
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
// makeMethods ensures that all concrete methods of type T are
|
||||
// generated. It is equivalent to calling prog.Method() on all
|
||||
// members of T.methodSet(), but acquires fewer locks.
|
||||
//
|
||||
// It reports whether the type's (concrete) method set is non-empty.
|
||||
//
|
||||
// Thread-safe.
|
||||
//
|
||||
// EXCLUSIVE_LOCKS_ACQUIRED(prog.methodsMu)
|
||||
//
|
||||
func (prog *Program) makeMethods(T types.Type) bool {
|
||||
if isInterface(T) {
|
||||
return false // abstract method
|
||||
}
|
||||
tmset := prog.MethodSets.MethodSet(T)
|
||||
n := tmset.Len()
|
||||
if n == 0 {
|
||||
return false // empty (common case)
|
||||
}
|
||||
|
||||
if prog.mode&LogSource != 0 {
|
||||
defer logStack("makeMethods %s", T)()
|
||||
}
|
||||
|
||||
prog.methodsMu.Lock()
|
||||
defer prog.methodsMu.Unlock()
|
||||
|
||||
mset := prog.createMethodSet(T)
|
||||
if !mset.complete {
|
||||
mset.complete = true
|
||||
for i := 0; i < n; i++ {
|
||||
prog.addMethod(mset, tmset.At(i))
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// TypesWithMethodSets 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.
|
||||
//
|
||||
// It is the union of pkg.TypesWithMethodSets() for all pkg in
|
||||
// prog.AllPackages().
|
||||
//
|
||||
// Thread-safe.
|
||||
//
|
||||
// EXCLUSIVE_LOCKS_ACQUIRED(prog.methodsMu)
|
||||
//
|
||||
func (prog *Program) TypesWithMethodSets() []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
|
||||
}
|
||||
|
||||
// TypesWithMethodSets returns an unordered slice containing the set
|
||||
// of all concrete types referenced within package pkg and not
|
||||
// belonging to some other package, for which a complete (non-empty)
|
||||
// method set is required at run-time.
|
||||
//
|
||||
// A type belongs to a package if it is a named type or a pointer to a
|
||||
// named type, and the name was defined in that package. All other
|
||||
// types belong to no package.
|
||||
//
|
||||
// A type may appear in the TypesWithMethodSets() set of multiple
|
||||
// distinct packages if that type belongs to no package. Typical
|
||||
// compilers emit method sets for such types multiple times (using
|
||||
// weak symbols) into each package that references them, with the
|
||||
// linker performing duplicate elimination.
|
||||
//
|
||||
// This set includes the types of all operands of some MakeInterface
|
||||
// instruction, the types of all exported members of some package, and
|
||||
// all types that are subcomponents, since even types that aren't used
|
||||
// directly may be derived via reflection.
|
||||
//
|
||||
// Callers must not mutate the result.
|
||||
//
|
||||
func (pkg *Package) TypesWithMethodSets() []types.Type {
|
||||
return pkg.methodSets
|
||||
}
|
||||
|
||||
// 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())
|
||||
}
|
||||
|
||||
// -- wrappers ---------------------------------------------------------
|
||||
// -- wrappers -----------------------------------------------------------
|
||||
|
||||
// makeWrapper returns a synthetic method that delegates to the
|
||||
// declared method denoted by meth.Obj(), first performing any
|
||||
|
@ -229,15 +42,15 @@ func (prog *Program) declaredFunc(obj *types.Func) *Function {
|
|||
//
|
||||
// EXCLUSIVE_LOCKS_REQUIRED(prog.methodsMu)
|
||||
//
|
||||
func makeWrapper(prog *Program, meth *types.Selection) *Function {
|
||||
obj := meth.Obj().(*types.Func) // the declared function
|
||||
sig := meth.Type().(*types.Signature) // type of this wrapper
|
||||
func makeWrapper(prog *Program, sel *types.Selection) *Function {
|
||||
obj := sel.Obj().(*types.Func) // the declared function
|
||||
sig := sel.Type().(*types.Signature) // type of this wrapper
|
||||
|
||||
var recv *types.Var // wrapper's receiver or thunk's params[0]
|
||||
name := obj.Name()
|
||||
var description string
|
||||
var start int // first regular param
|
||||
if meth.Kind() == types.MethodExpr {
|
||||
if sel.Kind() == types.MethodExpr {
|
||||
name += "$thunk"
|
||||
description = "thunk"
|
||||
recv = sig.Params().At(0)
|
||||
|
@ -247,13 +60,13 @@ func makeWrapper(prog *Program, meth *types.Selection) *Function {
|
|||
recv = sig.Recv()
|
||||
}
|
||||
|
||||
description = fmt.Sprintf("%s for %s", description, meth.Obj())
|
||||
description = fmt.Sprintf("%s for %s", description, sel.Obj())
|
||||
if prog.mode&LogSource != 0 {
|
||||
defer logStack("make %s to (%s)", description, recv.Type())()
|
||||
}
|
||||
fn := &Function{
|
||||
name: name,
|
||||
method: meth,
|
||||
method: sel,
|
||||
object: obj,
|
||||
Signature: sig,
|
||||
Synthetic: description,
|
||||
|
@ -264,10 +77,10 @@ func makeWrapper(prog *Program, meth *types.Selection) *Function {
|
|||
fn.addSpilledParam(recv)
|
||||
createParams(fn, start)
|
||||
|
||||
indices := meth.Index()
|
||||
indices := sel.Index()
|
||||
|
||||
var v Value = fn.Locals[0] // spilled receiver
|
||||
if isPointer(meth.Recv()) {
|
||||
if isPointer(sel.Recv()) {
|
||||
v = emitLoad(fn, v)
|
||||
|
||||
// For simple indirection wrappers, perform an informative nil-check:
|
||||
|
@ -277,13 +90,13 @@ func makeWrapper(prog *Program, meth *types.Selection) *Function {
|
|||
c.Call.Value = &Builtin{
|
||||
name: "ssa:wrapnilchk",
|
||||
sig: types.NewSignature(nil, nil,
|
||||
types.NewTuple(anonVar(meth.Recv()), anonVar(tString), anonVar(tString)),
|
||||
types.NewTuple(anonVar(meth.Recv())), false),
|
||||
types.NewTuple(anonVar(sel.Recv()), anonVar(tString), anonVar(tString)),
|
||||
types.NewTuple(anonVar(sel.Recv())), false),
|
||||
}
|
||||
c.Call.Args = []Value{
|
||||
v,
|
||||
stringConst(deref(meth.Recv()).String()),
|
||||
stringConst(meth.Obj().Name()),
|
||||
stringConst(deref(sel.Recv()).String()),
|
||||
stringConst(sel.Obj().Name()),
|
||||
}
|
||||
c.setType(v.Type())
|
||||
v = fn.emit(&c)
|
||||
|
@ -464,16 +277,6 @@ func changeRecv(s *types.Signature, recv *types.Var) *types.Signature {
|
|||
return types.NewSignature(nil, recv, s.Params(), s.Results(), s.Variadic())
|
||||
}
|
||||
|
||||
// recvType returns the receiver type of method obj.
|
||||
func recvType(obj *types.Func) types.Type {
|
||||
return obj.Type().(*types.Signature).Recv().Type()
|
||||
}
|
||||
|
||||
func isInterface(T types.Type) bool {
|
||||
_, ok := T.Underlying().(*types.Interface)
|
||||
return ok
|
||||
}
|
||||
|
||||
// selectionKey is like types.Selection but a usable map key.
|
||||
type selectionKey struct {
|
||||
kind types.SelectionKind
|
Loading…
Reference in New Issue