github.com/rsc/go@v0.0.0-20150416155037-e040fd465409/src/go/types/methodset.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 // This file implements method sets. 6 7 package types 8 9 import ( 10 "bytes" 11 "fmt" 12 "sort" 13 ) 14 15 // A MethodSet is an ordered set of concrete or abstract (interface) methods; 16 // a method is a MethodVal selection, and they are ordered by ascending m.Obj().Id(). 17 // The zero value for a MethodSet is a ready-to-use empty method set. 18 type MethodSet struct { 19 list []*Selection 20 } 21 22 func (s *MethodSet) String() string { 23 if s.Len() == 0 { 24 return "MethodSet {}" 25 } 26 27 var buf bytes.Buffer 28 fmt.Fprintln(&buf, "MethodSet {") 29 for _, f := range s.list { 30 fmt.Fprintf(&buf, "\t%s\n", f) 31 } 32 fmt.Fprintln(&buf, "}") 33 return buf.String() 34 } 35 36 // Len returns the number of methods in s. 37 func (s *MethodSet) Len() int { return len(s.list) } 38 39 // At returns the i'th method in s for 0 <= i < s.Len(). 40 func (s *MethodSet) At(i int) *Selection { return s.list[i] } 41 42 // Lookup returns the method with matching package and name, or nil if not found. 43 func (s *MethodSet) Lookup(pkg *Package, name string) *Selection { 44 if s.Len() == 0 { 45 return nil 46 } 47 48 key := Id(pkg, name) 49 i := sort.Search(len(s.list), func(i int) bool { 50 m := s.list[i] 51 return m.obj.Id() >= key 52 }) 53 if i < len(s.list) { 54 m := s.list[i] 55 if m.obj.Id() == key { 56 return m 57 } 58 } 59 return nil 60 } 61 62 // Shared empty method set. 63 var emptyMethodSet MethodSet 64 65 // NewMethodSet returns the method set for the given type T. It 66 // always returns a non-nil method set, even if it is empty. 67 // 68 // A MethodSetCache handles repeat queries more efficiently. 69 // 70 func NewMethodSet(T Type) *MethodSet { 71 // WARNING: The code in this function is extremely subtle - do not modify casually! 72 // This function and lookupFieldOrMethod should be kept in sync. 73 74 // method set up to the current depth, allocated lazily 75 var base methodSet 76 77 typ, isPtr := deref(T) 78 named, _ := typ.(*Named) 79 80 // *typ where typ is an interface has no methods. 81 if isPtr { 82 utyp := typ 83 if named != nil { 84 utyp = named.underlying 85 } 86 if _, ok := utyp.(*Interface); ok { 87 return &emptyMethodSet 88 } 89 } 90 91 // Start with typ as single entry at shallowest depth. 92 // If typ is not a named type, insert a nil type instead. 93 current := []embeddedType{{named, nil, isPtr, false}} 94 95 // named types that we have seen already, allocated lazily 96 var seen map[*Named]bool 97 98 // collect methods at current depth 99 for len(current) > 0 { 100 var next []embeddedType // embedded types found at current depth 101 102 // field and method sets at current depth, allocated lazily 103 var fset fieldSet 104 var mset methodSet 105 106 for _, e := range current { 107 // The very first time only, e.typ may be nil. 108 // In this case, we don't have a named type and 109 // we simply continue with the underlying type. 110 if e.typ != nil { 111 if seen[e.typ] { 112 // We have seen this type before, at a more shallow depth 113 // (note that multiples of this type at the current depth 114 // were consolidated before). The type at that depth shadows 115 // this same type at the current depth, so we can ignore 116 // this one. 117 continue 118 } 119 if seen == nil { 120 seen = make(map[*Named]bool) 121 } 122 seen[e.typ] = true 123 124 mset = mset.add(e.typ.methods, e.index, e.indirect, e.multiples) 125 126 // continue with underlying type 127 typ = e.typ.underlying 128 } 129 130 switch t := typ.(type) { 131 case *Struct: 132 for i, f := range t.fields { 133 fset = fset.add(f, e.multiples) 134 135 // Embedded fields are always of the form T or *T where 136 // T is a named type. If typ appeared multiple times at 137 // this depth, f.Type appears multiple times at the next 138 // depth. 139 if f.anonymous { 140 // Ignore embedded basic types - only user-defined 141 // named types can have methods or struct fields. 142 typ, isPtr := deref(f.typ) 143 if t, _ := typ.(*Named); t != nil { 144 next = append(next, embeddedType{t, concat(e.index, i), e.indirect || isPtr, e.multiples}) 145 } 146 } 147 } 148 149 case *Interface: 150 mset = mset.add(t.allMethods, e.index, true, e.multiples) 151 } 152 } 153 154 // Add methods and collisions at this depth to base if no entries with matching 155 // names exist already. 156 for k, m := range mset { 157 if _, found := base[k]; !found { 158 // Fields collide with methods of the same name at this depth. 159 if _, found := fset[k]; found { 160 m = nil // collision 161 } 162 if base == nil { 163 base = make(methodSet) 164 } 165 base[k] = m 166 } 167 } 168 169 // Multiple fields with matching names collide at this depth and shadow all 170 // entries further down; add them as collisions to base if no entries with 171 // matching names exist already. 172 for k, f := range fset { 173 if f == nil { 174 if _, found := base[k]; !found { 175 if base == nil { 176 base = make(methodSet) 177 } 178 base[k] = nil // collision 179 } 180 } 181 } 182 183 current = consolidateMultiples(next) 184 } 185 186 if len(base) == 0 { 187 return &emptyMethodSet 188 } 189 190 // collect methods 191 var list []*Selection 192 for _, m := range base { 193 if m != nil { 194 m.recv = T 195 list = append(list, m) 196 } 197 } 198 sort.Sort(byUniqueName(list)) 199 return &MethodSet{list} 200 } 201 202 // A fieldSet is a set of fields and name collisions. 203 // A collision indicates that multiple fields with the 204 // same unique id appeared. 205 type fieldSet map[string]*Var // a nil entry indicates a name collision 206 207 // Add adds field f to the field set s. 208 // If multiples is set, f appears multiple times 209 // and is treated as a collision. 210 func (s fieldSet) add(f *Var, multiples bool) fieldSet { 211 if s == nil { 212 s = make(fieldSet) 213 } 214 key := f.Id() 215 // if f is not in the set, add it 216 if !multiples { 217 if _, found := s[key]; !found { 218 s[key] = f 219 return s 220 } 221 } 222 s[key] = nil // collision 223 return s 224 } 225 226 // A methodSet is a set of methods and name collisions. 227 // A collision indicates that multiple methods with the 228 // same unique id appeared. 229 type methodSet map[string]*Selection // a nil entry indicates a name collision 230 231 // Add adds all functions in list to the method set s. 232 // If multiples is set, every function in list appears multiple times 233 // and is treated as a collision. 234 func (s methodSet) add(list []*Func, index []int, indirect bool, multiples bool) methodSet { 235 if len(list) == 0 { 236 return s 237 } 238 if s == nil { 239 s = make(methodSet) 240 } 241 for i, f := range list { 242 key := f.Id() 243 // if f is not in the set, add it 244 if !multiples { 245 // TODO(gri) A found method may not be added because it's not in the method set 246 // (!indirect && ptrRecv(f)). A 2nd method on the same level may be in the method 247 // set and may not collide with the first one, thus leading to a false positive. 248 // Is that possible? Investigate. 249 if _, found := s[key]; !found && (indirect || !ptrRecv(f)) { 250 s[key] = &Selection{MethodVal, nil, f, concat(index, i), indirect} 251 continue 252 } 253 } 254 s[key] = nil // collision 255 } 256 return s 257 } 258 259 // ptrRecv reports whether the receiver is of the form *T. 260 // The receiver must exist. 261 func ptrRecv(f *Func) bool { 262 _, isPtr := deref(f.typ.(*Signature).recv.typ) 263 return isPtr 264 } 265 266 // byUniqueName function lists can be sorted by their unique names. 267 type byUniqueName []*Selection 268 269 func (a byUniqueName) Len() int { return len(a) } 270 func (a byUniqueName) Less(i, j int) bool { return a[i].obj.Id() < a[j].obj.Id() } 271 func (a byUniqueName) Swap(i, j int) { a[i], a[j] = a[j], a[i] }