go/ssa: simplify TypesWithMethodSets
Details: - rename (*Program).TypesWithMethodSets() to RuntimeTypes() - delete (*Package).TypesWithMethodSets() method and simplify - move code to methods.go - update test to use 1-2% improvement in space and time (though I barely trust this data because the GC at tip is in such terrible state). Change-Id: I38eab78b11e0ad0ff16e0530e775b6ff6a2ab246 Reviewed-on: https://go-review.googlesource.com/3148 Reviewed-by: Robert Griesemer <gri@golang.org>
This commit is contained in:
parent
e079f6c632
commit
f011631cea
|
@ -1262,7 +1262,7 @@ func (a *analysis) generate() {
|
||||||
|
|
||||||
// Create nodes and constraints for all methods of all types
|
// Create nodes and constraints for all methods of all types
|
||||||
// that are dynamically accessible via reflection or interfaces.
|
// that are dynamically accessible via reflection or interfaces.
|
||||||
for _, T := range a.prog.TypesWithMethodSets() {
|
for _, T := range a.prog.RuntimeTypes() {
|
||||||
a.genMethodsOf(T)
|
a.genMethodsOf(T)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2193,7 +2193,7 @@ func (p *Package) Build() {
|
||||||
// that would require package creation in topological order.
|
// that would require package creation in topological order.
|
||||||
for name, mem := range p.Members {
|
for name, mem := range p.Members {
|
||||||
if ast.IsExported(name) {
|
if ast.IsExported(name) {
|
||||||
p.needMethodsOf(mem.Type())
|
p.Prog.needMethodsOf(mem.Type())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if p.Prog.mode&LogSource != 0 {
|
if p.Prog.mode&LogSource != 0 {
|
||||||
|
@ -2301,129 +2301,3 @@ func (p *Package) typeOf(e ast.Expr) types.Type {
|
||||||
panic(fmt.Sprintf("no type for %T @ %s",
|
panic(fmt.Sprintf("no type for %T @ %s",
|
||||||
e, p.Prog.Fset.Position(e.Pos())))
|
e, p.Prog.Fset.Position(e.Pos())))
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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. Do we need to maintain a distinct needRTTI and methodSets per
|
|
||||||
// package? Using just one in the program might be much faster.
|
|
||||||
//
|
|
||||||
func (p *Package) needMethodsOf(T types.Type) {
|
|
||||||
p.methodsMu.Lock()
|
|
||||||
p.needMethods(T, false)
|
|
||||||
p.methodsMu.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Precondition: T is not a method signature (*Signature with Recv()!=nil).
|
|
||||||
// Precondition: the p.methodsMu lock is held.
|
|
||||||
// Recursive case: skip => don't call makeMethods(T).
|
|
||||||
func (p *Package) needMethods(T types.Type, skip bool) {
|
|
||||||
// Each package maintains its own set of types it has visited.
|
|
||||||
if prevSkip, ok := p.needRTTI.At(T).(bool); ok {
|
|
||||||
// needMethods(T) was previously called
|
|
||||||
if !prevSkip || skip {
|
|
||||||
return // already seen, with same or false 'skip' value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
p.needRTTI.Set(T, skip)
|
|
||||||
|
|
||||||
// Prune the recursion if we find a named or *named type
|
|
||||||
// belonging to another package.
|
|
||||||
var n *types.Named
|
|
||||||
switch T := T.(type) {
|
|
||||||
case *types.Named:
|
|
||||||
n = T
|
|
||||||
case *types.Pointer:
|
|
||||||
n, _ = T.Elem().(*types.Named)
|
|
||||||
}
|
|
||||||
if n != nil {
|
|
||||||
owner := n.Obj().Pkg()
|
|
||||||
if owner == nil {
|
|
||||||
return // built-in error type
|
|
||||||
}
|
|
||||||
if owner != p.Object {
|
|
||||||
return // belongs to another package
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// All the actual method sets live in the Program so that
|
|
||||||
// multiple packages can share a single copy in memory of the
|
|
||||||
// symbols that would be compiled into multiple packages (as
|
|
||||||
// weak symbols).
|
|
||||||
if !skip && p.Prog.makeMethods(T) {
|
|
||||||
p.methodSets = append(p.methodSets, T)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Recursion over signatures of each method.
|
|
||||||
tmset := p.Prog.MethodSets.MethodSet(T)
|
|
||||||
for i := 0; i < tmset.Len(); i++ {
|
|
||||||
sig := tmset.At(i).Type().(*types.Signature)
|
|
||||||
p.needMethods(sig.Params(), false)
|
|
||||||
p.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:
|
|
||||||
p.needMethods(t.Elem(), false)
|
|
||||||
|
|
||||||
case *types.Slice:
|
|
||||||
p.needMethods(t.Elem(), false)
|
|
||||||
|
|
||||||
case *types.Chan:
|
|
||||||
p.needMethods(t.Elem(), false)
|
|
||||||
|
|
||||||
case *types.Map:
|
|
||||||
p.needMethods(t.Key(), false)
|
|
||||||
p.needMethods(t.Elem(), false)
|
|
||||||
|
|
||||||
case *types.Signature:
|
|
||||||
if t.Recv() != nil {
|
|
||||||
panic(fmt.Sprintf("Signature %s has Recv %s", t, t.Recv()))
|
|
||||||
}
|
|
||||||
p.needMethods(t.Params(), false)
|
|
||||||
p.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.
|
|
||||||
p.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.
|
|
||||||
p.needMethods(t.Underlying(), true)
|
|
||||||
|
|
||||||
case *types.Array:
|
|
||||||
p.needMethods(t.Elem(), false)
|
|
||||||
|
|
||||||
case *types.Struct:
|
|
||||||
for i, n := 0, t.NumFields(); i < n; i++ {
|
|
||||||
p.needMethods(t.Field(i).Type(), false)
|
|
||||||
}
|
|
||||||
|
|
||||||
case *types.Tuple:
|
|
||||||
for i, n := 0, t.Len(); i < n; i++ {
|
|
||||||
p.needMethods(t.At(i).Type(), false)
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
panic(T)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -151,8 +151,8 @@ func main() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestTypesWithMethodSets tests that Package.TypesWithMethodSets includes all necessary types.
|
// TestRuntimeTypes tests that (*Program).RuntimeTypes() includes all necessary types.
|
||||||
func TestTypesWithMethodSets(t *testing.T) {
|
func TestRuntimeTypes(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
input string
|
input string
|
||||||
want []string
|
want []string
|
||||||
|
@ -167,7 +167,7 @@ func TestTypesWithMethodSets(t *testing.T) {
|
||||||
},
|
},
|
||||||
// Subcomponents of type of exported package-level var are needed.
|
// Subcomponents of type of exported package-level var are needed.
|
||||||
{`package C; import "bytes"; var V struct {*bytes.Buffer}`,
|
{`package C; import "bytes"; var V struct {*bytes.Buffer}`,
|
||||||
[]string{"*struct{*bytes.Buffer}", "struct{*bytes.Buffer}"},
|
[]string{"*bytes.Buffer", "*struct{*bytes.Buffer}", "struct{*bytes.Buffer}"},
|
||||||
},
|
},
|
||||||
// Subcomponents of type of unexported package-level var are not needed.
|
// Subcomponents of type of unexported package-level var are not needed.
|
||||||
{`package D; import "bytes"; var v struct {*bytes.Buffer}`,
|
{`package D; import "bytes"; var v struct {*bytes.Buffer}`,
|
||||||
|
@ -175,7 +175,7 @@ func TestTypesWithMethodSets(t *testing.T) {
|
||||||
},
|
},
|
||||||
// Subcomponents of type of exported package-level function are needed.
|
// Subcomponents of type of exported package-level function are needed.
|
||||||
{`package E; import "bytes"; func F(struct {*bytes.Buffer}) {}`,
|
{`package E; import "bytes"; func F(struct {*bytes.Buffer}) {}`,
|
||||||
[]string{"struct{*bytes.Buffer}"},
|
[]string{"*bytes.Buffer", "struct{*bytes.Buffer}"},
|
||||||
},
|
},
|
||||||
// Subcomponents of type of unexported package-level function are not needed.
|
// Subcomponents of type of unexported package-level function are not needed.
|
||||||
{`package F; import "bytes"; func f(struct {*bytes.Buffer}) {}`,
|
{`package F; import "bytes"; func f(struct {*bytes.Buffer}) {}`,
|
||||||
|
@ -187,11 +187,11 @@ func TestTypesWithMethodSets(t *testing.T) {
|
||||||
},
|
},
|
||||||
// ...unless used by MakeInterface.
|
// ...unless used by MakeInterface.
|
||||||
{`package G2; import "bytes"; type x struct{}; func (x) G(struct {*bytes.Buffer}) {}; var v interface{} = x{}`,
|
{`package G2; import "bytes"; type x struct{}; func (x) G(struct {*bytes.Buffer}) {}; var v interface{} = x{}`,
|
||||||
[]string{"*p.x", "p.x", "struct{*bytes.Buffer}"},
|
[]string{"*bytes.Buffer", "*p.x", "p.x", "struct{*bytes.Buffer}"},
|
||||||
},
|
},
|
||||||
// Subcomponents of type of unexported method are not needed.
|
// Subcomponents of type of unexported method are not needed.
|
||||||
{`package I; import "bytes"; type X struct{}; func (X) G(struct {*bytes.Buffer}) {}`,
|
{`package I; import "bytes"; type X struct{}; func (X) G(struct {*bytes.Buffer}) {}`,
|
||||||
[]string{"*p.X", "p.X", "struct{*bytes.Buffer}"},
|
[]string{"*bytes.Buffer", "*p.X", "p.X", "struct{*bytes.Buffer}"},
|
||||||
},
|
},
|
||||||
// Local types aren't needed.
|
// Local types aren't needed.
|
||||||
{`package J; import "bytes"; func f() { type T struct {*bytes.Buffer}; var t T; _ = t }`,
|
{`package J; import "bytes"; func f() { type T struct {*bytes.Buffer}; var t T; _ = t }`,
|
||||||
|
@ -199,11 +199,11 @@ func TestTypesWithMethodSets(t *testing.T) {
|
||||||
},
|
},
|
||||||
// ...unless used by MakeInterface.
|
// ...unless used by MakeInterface.
|
||||||
{`package K; import "bytes"; func f() { type T struct {*bytes.Buffer}; _ = interface{}(T{}) }`,
|
{`package K; import "bytes"; func f() { type T struct {*bytes.Buffer}; _ = interface{}(T{}) }`,
|
||||||
[]string{"*p.T", "p.T"},
|
[]string{"*bytes.Buffer", "*p.T", "p.T"},
|
||||||
},
|
},
|
||||||
// Types used as operand of MakeInterface are needed.
|
// Types used as operand of MakeInterface are needed.
|
||||||
{`package L; import "bytes"; func f() { _ = interface{}(struct{*bytes.Buffer}{}) }`,
|
{`package L; import "bytes"; func f() { _ = interface{}(struct{*bytes.Buffer}{}) }`,
|
||||||
[]string{"struct{*bytes.Buffer}"},
|
[]string{"*bytes.Buffer", "struct{*bytes.Buffer}"},
|
||||||
},
|
},
|
||||||
// MakeInterface is optimized away when storing to a blank.
|
// MakeInterface is optimized away when storing to a blank.
|
||||||
{`package M; import "bytes"; var _ interface{} = struct{*bytes.Buffer}{}`,
|
{`package M; import "bytes"; var _ interface{} = struct{*bytes.Buffer}{}`,
|
||||||
|
@ -226,17 +226,17 @@ func TestTypesWithMethodSets(t *testing.T) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
prog := ssa.Create(iprog, ssa.SanityCheckFunctions)
|
prog := ssa.Create(iprog, ssa.SanityCheckFunctions)
|
||||||
mainPkg := prog.Package(iprog.Created[0].Pkg)
|
|
||||||
prog.BuildAll()
|
prog.BuildAll()
|
||||||
|
|
||||||
var typstrs []string
|
var typstrs []string
|
||||||
for _, T := range mainPkg.TypesWithMethodSets() {
|
for _, T := range prog.RuntimeTypes() {
|
||||||
typstrs = append(typstrs, T.String())
|
typstrs = append(typstrs, T.String())
|
||||||
}
|
}
|
||||||
sort.Strings(typstrs)
|
sort.Strings(typstrs)
|
||||||
|
|
||||||
if !reflect.DeepEqual(typstrs, test.want) {
|
if !reflect.DeepEqual(typstrs, test.want) {
|
||||||
t.Errorf("test 'package %s': got %q, want %q", f.Name.Name, typstrs, test.want)
|
t.Errorf("test 'package %s': got %q, want %q",
|
||||||
|
f.Name.Name, typstrs, test.want)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -208,7 +208,7 @@ func emitConv(f *Function, val Value, typ types.Type) Value {
|
||||||
val = emitConv(f, val, DefaultType(ut_src))
|
val = emitConv(f, val, DefaultType(ut_src))
|
||||||
}
|
}
|
||||||
|
|
||||||
f.Pkg.needMethodsOf(val.Type())
|
f.Pkg.Prog.needMethodsOf(val.Type())
|
||||||
mi := &MakeInterface{X: val}
|
mi := &MakeInterface{X: val}
|
||||||
mi.setType(typ)
|
mi.setType(typ)
|
||||||
return f.emit(mi)
|
return f.emit(mi)
|
||||||
|
|
|
@ -55,44 +55,6 @@ func (prog *Program) LookupMethod(T types.Type, pkg *types.Package, name string)
|
||||||
return prog.Method(sel)
|
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.
|
// methodSet contains the (concrete) methods of a non-interface type.
|
||||||
type methodSet struct {
|
type methodSet struct {
|
||||||
mapping map[string]*Function // populated lazily
|
mapping map[string]*Function // populated lazily
|
||||||
|
@ -135,18 +97,15 @@ func (prog *Program) addMethod(mset *methodSet, sel *types.Selection) *Function
|
||||||
return fn
|
return fn
|
||||||
}
|
}
|
||||||
|
|
||||||
// TypesWithMethodSets returns a new unordered slice containing all
|
// RuntimeTypes returns a new unordered slice containing all
|
||||||
// concrete types in the program for which a complete (non-empty)
|
// concrete types in the program for which a complete (non-empty)
|
||||||
// method set is required at run-time.
|
// method set is required at run-time.
|
||||||
//
|
//
|
||||||
// It is the union of pkg.TypesWithMethodSets() for all pkg in
|
|
||||||
// prog.AllPackages().
|
|
||||||
//
|
|
||||||
// Thread-safe.
|
// Thread-safe.
|
||||||
//
|
//
|
||||||
// EXCLUSIVE_LOCKS_ACQUIRED(prog.methodsMu)
|
// EXCLUSIVE_LOCKS_ACQUIRED(prog.methodsMu)
|
||||||
//
|
//
|
||||||
func (prog *Program) TypesWithMethodSets() []types.Type {
|
func (prog *Program) RuntimeTypes() []types.Type {
|
||||||
prog.methodsMu.Lock()
|
prog.methodsMu.Lock()
|
||||||
defer prog.methodsMu.Unlock()
|
defer prog.methodsMu.Unlock()
|
||||||
|
|
||||||
|
@ -159,33 +118,6 @@ func (prog *Program) TypesWithMethodSets() []types.Type {
|
||||||
return res
|
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 {
|
|
||||||
// pkg.methodsMu not required; concurrent (build) phase is over.
|
|
||||||
return pkg.methodSets
|
|
||||||
}
|
|
||||||
|
|
||||||
// declaredFunc returns the concrete function/method denoted by obj.
|
// declaredFunc returns the concrete function/method denoted by obj.
|
||||||
// Panic ensues if there is none.
|
// Panic ensues if there is none.
|
||||||
//
|
//
|
||||||
|
@ -195,3 +127,117 @@ func (prog *Program) declaredFunc(obj *types.Func) *Function {
|
||||||
}
|
}
|
||||||
panic("no concrete method: " + obj.String())
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -28,11 +28,12 @@ type Program struct {
|
||||||
mode BuilderMode // set of mode bits for SSA construction
|
mode BuilderMode // set of mode bits for SSA construction
|
||||||
MethodSets types.MethodSetCache // cache of type-checker's method-sets
|
MethodSets types.MethodSetCache // cache of type-checker's method-sets
|
||||||
|
|
||||||
methodsMu sync.Mutex // guards the following maps:
|
methodsMu sync.Mutex // guards the following maps:
|
||||||
methodSets typeutil.Map // maps type to its concrete methodSet
|
methodSets typeutil.Map // maps type to its concrete methodSet
|
||||||
canon typeutil.Map // type canonicalization map
|
runtimeTypes typeutil.Map // types for which rtypes are needed
|
||||||
bounds map[*types.Func]*Function // bounds for curried x.Method closures
|
canon typeutil.Map // type canonicalization map
|
||||||
thunks map[selectionKey]*Function // thunks for T.Method expressions
|
bounds map[*types.Func]*Function // bounds for curried x.Method closures
|
||||||
|
thunks map[selectionKey]*Function // thunks for T.Method expressions
|
||||||
}
|
}
|
||||||
|
|
||||||
// A Package is a single analyzed Go package containing Members for
|
// A Package is a single analyzed Go package containing Members for
|
||||||
|
@ -41,21 +42,18 @@ type Program struct {
|
||||||
// type-specific accessor methods Func, Type, Var and Const.
|
// type-specific accessor methods Func, Type, Var and Const.
|
||||||
//
|
//
|
||||||
type Package struct {
|
type Package struct {
|
||||||
Prog *Program // the owning program
|
Prog *Program // the owning program
|
||||||
Object *types.Package // the type checker's package object for this package
|
Object *types.Package // the type checker's package object for this package
|
||||||
Members map[string]Member // all package members keyed by name
|
Members map[string]Member // all package members keyed by name
|
||||||
methodsMu sync.Mutex // guards needRTTI and methodSets
|
values map[types.Object]Value // package members (incl. types and methods), keyed by object
|
||||||
methodSets []types.Type // types whose method sets are included in this package
|
init *Function // Func("init"); the package's init function
|
||||||
values map[types.Object]Value // package members (incl. types and methods), keyed by object
|
debug bool // include full debug info in this package.
|
||||||
init *Function // Func("init"); the package's init function
|
|
||||||
debug bool // include full debug info in this package.
|
|
||||||
|
|
||||||
// The following fields are set transiently, then cleared
|
// The following fields are set transiently, then cleared
|
||||||
// after building.
|
// after building.
|
||||||
started int32 // atomically tested and set at start of build phase
|
started int32 // atomically tested and set at start of build phase
|
||||||
ninit int32 // number of init functions
|
ninit int32 // number of init functions
|
||||||
info *loader.PackageInfo // package ASTs and type information
|
info *loader.PackageInfo // package ASTs and type information
|
||||||
needRTTI typeutil.Map // types for which runtime type info is needed
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// A Member is a member of a Go package, implemented by *NamedConst,
|
// A Member is a member of a Go package, implemented by *NamedConst,
|
||||||
|
|
|
@ -41,7 +41,7 @@ func (visit *visitor) program() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, T := range visit.prog.TypesWithMethodSets() {
|
for _, T := range visit.prog.RuntimeTypes() {
|
||||||
mset := visit.prog.MethodSets.MethodSet(T)
|
mset := visit.prog.MethodSets.MethodSet(T)
|
||||||
for i, n := 0, mset.Len(); i < n; i++ {
|
for i, n := 0, mset.Len(); i < n; i++ {
|
||||||
visit.function(visit.prog.Method(mset.At(i)))
|
visit.function(visit.prog.Method(mset.At(i)))
|
||||||
|
|
Loading…
Reference in New Issue