blob: c60aa47b2f8a12625dc89b3de999c0f34f80fc28 [file] [log] [blame]
Keith Randall483cb612014-08-07 13:58:42 -07001// Copyright 2014 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
5package runtime
6
7import (
8 "unsafe"
9)
10
11const (
12 hashSize = 1009
13)
14
15var (
Russ Cox8ecb9a72014-08-27 23:32:49 -040016 ifaceLock mutex // lock for accessing hash
Keith Randall483cb612014-08-07 13:58:42 -070017 hash [hashSize]*itab
18)
19
20// fInterface is our standard non-empty interface. We use it instead
21// of interface{f()} in function prototypes because gofmt insists on
22// putting lots of newlines in the otherwise concise interface{f()}.
23type fInterface interface {
24 f()
25}
26
27func getitab(inter *interfacetype, typ *_type, canfail bool) *itab {
28 if len(inter.mhdr) == 0 {
Keith Randallb2a950b2014-12-27 20:58:00 -080029 throw("internal error - misuse of itab")
Keith Randall483cb612014-08-07 13:58:42 -070030 }
31
32 // easy case
33 x := typ.x
34 if x == nil {
35 if canfail {
36 return nil
37 }
Keith Randall1dd01632015-01-06 20:38:44 -080038 panic(&TypeAssertionError{"", *typ._string, *inter.typ._string, *inter.mhdr[0].name})
Keith Randall483cb612014-08-07 13:58:42 -070039 }
40
41 // compiler has provided some good hash codes for us.
42 h := inter.typ.hash
43 h += 17 * typ.hash
44 // TODO(rsc): h += 23 * x.mhash ?
45 h %= hashSize
46
47 // look twice - once without lock, once with.
48 // common case will be no lock contention.
49 var m *itab
50 var locked int
51 for locked = 0; locked < 2; locked++ {
52 if locked != 0 {
Russ Cox8ecb9a72014-08-27 23:32:49 -040053 lock(&ifaceLock)
Keith Randall483cb612014-08-07 13:58:42 -070054 }
Russ Coxd21638b2014-08-27 21:59:49 -040055 for m = (*itab)(atomicloadp(unsafe.Pointer(&hash[h]))); m != nil; m = m.link {
Keith Randall483cb612014-08-07 13:58:42 -070056 if m.inter == inter && m._type == typ {
57 if m.bad != 0 {
58 m = nil
59 if !canfail {
60 // this can only happen if the conversion
61 // was already done once using the , ok form
62 // and we have a cached negative result.
63 // the cached result doesn't record which
64 // interface function was missing, so jump
65 // down to the interface check, which will
66 // do more work but give a better error.
67 goto search
68 }
69 }
70 if locked != 0 {
Russ Cox8ecb9a72014-08-27 23:32:49 -040071 unlock(&ifaceLock)
Keith Randall483cb612014-08-07 13:58:42 -070072 }
73 return m
74 }
75 }
76 }
77
Keith Randall1dd01632015-01-06 20:38:44 -080078 m = (*itab)(persistentalloc(unsafe.Sizeof(itab{})+uintptr(len(inter.mhdr)-1)*ptrSize, 0, &memstats.other_sys))
Keith Randall483cb612014-08-07 13:58:42 -070079 m.inter = inter
80 m._type = typ
81
82search:
83 // both inter and typ have method sorted by name,
84 // and interface names are unique,
85 // so can iterate over both in lock step;
86 // the loop is O(ni+nt) not O(ni*nt).
87 ni := len(inter.mhdr)
88 nt := len(x.mhdr)
89 j := 0
90 for k := 0; k < ni; k++ {
Keith Randall1dd01632015-01-06 20:38:44 -080091 i := &inter.mhdr[k]
Keith Randall483cb612014-08-07 13:58:42 -070092 iname := i.name
93 ipkgpath := i.pkgpath
94 itype := i._type
95 for ; j < nt; j++ {
Keith Randall1dd01632015-01-06 20:38:44 -080096 t := &x.mhdr[j]
Keith Randall483cb612014-08-07 13:58:42 -070097 if t.mtyp == itype && t.name == iname && t.pkgpath == ipkgpath {
98 if m != nil {
Keith Randall1dd01632015-01-06 20:38:44 -080099 *(*unsafe.Pointer)(add(unsafe.Pointer(&m.fun[0]), uintptr(k)*ptrSize)) = t.ifn
Keith Randall483cb612014-08-07 13:58:42 -0700100 }
101 goto nextimethod
102 }
103 }
104 // didn't find method
105 if !canfail {
106 if locked != 0 {
Russ Cox8ecb9a72014-08-27 23:32:49 -0400107 unlock(&ifaceLock)
Keith Randall483cb612014-08-07 13:58:42 -0700108 }
109 panic(&TypeAssertionError{"", *typ._string, *inter.typ._string, *iname})
110 }
111 m.bad = 1
112 break
113 nextimethod:
114 }
115 if locked == 0 {
Keith Randallb2a950b2014-12-27 20:58:00 -0800116 throw("invalid itab locking")
Keith Randall483cb612014-08-07 13:58:42 -0700117 }
118 m.link = hash[h]
Russ Coxd21638b2014-08-27 21:59:49 -0400119 atomicstorep(unsafe.Pointer(&hash[h]), unsafe.Pointer(m))
Russ Cox8ecb9a72014-08-27 23:32:49 -0400120 unlock(&ifaceLock)
Keith Randall483cb612014-08-07 13:58:42 -0700121 if m.bad != 0 {
122 return nil
123 }
124 return m
125}
126
127func typ2Itab(t *_type, inter *interfacetype, cache **itab) *itab {
128 tab := getitab(inter, t, false)
Russ Coxd21638b2014-08-27 21:59:49 -0400129 atomicstorep(unsafe.Pointer(cache), unsafe.Pointer(tab))
Keith Randall483cb612014-08-07 13:58:42 -0700130 return tab
131}
132
133func convT2E(t *_type, elem unsafe.Pointer) (e interface{}) {
Keith Randall483cb612014-08-07 13:58:42 -0700134 ep := (*eface)(unsafe.Pointer(&e))
Russ Cox1806a572014-08-18 21:13:11 -0400135 if isDirectIface(t) {
Keith Randall483cb612014-08-07 13:58:42 -0700136 ep._type = t
Russ Cox54bb4dc2014-12-29 10:07:47 -0500137 typedmemmove(t, unsafe.Pointer(&ep.data), elem)
Keith Randall483cb612014-08-07 13:58:42 -0700138 } else {
139 x := newobject(t)
140 // TODO: We allocate a zeroed object only to overwrite it with
141 // actual data. Figure out how to avoid zeroing. Also below in convT2I.
Russ Cox54bb4dc2014-12-29 10:07:47 -0500142 typedmemmove(t, x, elem)
Keith Randall483cb612014-08-07 13:58:42 -0700143 ep._type = t
144 ep.data = x
145 }
146 return
147}
148
149func convT2I(t *_type, inter *interfacetype, cache **itab, elem unsafe.Pointer) (i fInterface) {
Russ Coxd21638b2014-08-27 21:59:49 -0400150 tab := (*itab)(atomicloadp(unsafe.Pointer(cache)))
Keith Randall483cb612014-08-07 13:58:42 -0700151 if tab == nil {
152 tab = getitab(inter, t, false)
Russ Coxd21638b2014-08-27 21:59:49 -0400153 atomicstorep(unsafe.Pointer(cache), unsafe.Pointer(tab))
Keith Randall483cb612014-08-07 13:58:42 -0700154 }
Keith Randall483cb612014-08-07 13:58:42 -0700155 pi := (*iface)(unsafe.Pointer(&i))
Russ Cox1806a572014-08-18 21:13:11 -0400156 if isDirectIface(t) {
Keith Randall483cb612014-08-07 13:58:42 -0700157 pi.tab = tab
Russ Cox54bb4dc2014-12-29 10:07:47 -0500158 typedmemmove(t, unsafe.Pointer(&pi.data), elem)
Keith Randall483cb612014-08-07 13:58:42 -0700159 } else {
160 x := newobject(t)
Russ Cox54bb4dc2014-12-29 10:07:47 -0500161 typedmemmove(t, x, elem)
Keith Randall483cb612014-08-07 13:58:42 -0700162 pi.tab = tab
163 pi.data = x
164 }
165 return
166}
167
Russ Cox4224d812015-03-20 00:06:10 -0400168func panicdottype(have, want, iface *_type) {
169 haveString := ""
170 if have != nil {
171 haveString = *have._string
172 }
173 panic(&TypeAssertionError{*iface._string, haveString, *want._string, ""})
174}
175
Russ Coxccdb5092014-12-29 16:15:05 -0500176func assertI2T(t *_type, i fInterface, r unsafe.Pointer) {
Keith Randall483cb612014-08-07 13:58:42 -0700177 ip := (*iface)(unsafe.Pointer(&i))
178 tab := ip.tab
179 if tab == nil {
180 panic(&TypeAssertionError{"", "", *t._string, ""})
181 }
182 if tab._type != t {
183 panic(&TypeAssertionError{*tab.inter.typ._string, *tab._type._string, *t._string, ""})
184 }
Russ Coxccdb5092014-12-29 16:15:05 -0500185 if r != nil {
186 if isDirectIface(t) {
187 writebarrierptr((*uintptr)(r), uintptr(ip.data))
188 } else {
189 typedmemmove(t, r, ip.data)
190 }
Keith Randall483cb612014-08-07 13:58:42 -0700191 }
Keith Randall483cb612014-08-07 13:58:42 -0700192}
193
Russ Coxccdb5092014-12-29 16:15:05 -0500194func assertI2T2(t *_type, i fInterface, r unsafe.Pointer) bool {
Keith Randall483cb612014-08-07 13:58:42 -0700195 ip := (*iface)(unsafe.Pointer(&i))
Keith Randall483cb612014-08-07 13:58:42 -0700196 tab := ip.tab
197 if tab == nil || tab._type != t {
Russ Coxccdb5092014-12-29 16:15:05 -0500198 if r != nil {
199 memclr(r, uintptr(t.size))
200 }
201 return false
Keith Randall483cb612014-08-07 13:58:42 -0700202 }
Russ Coxccdb5092014-12-29 16:15:05 -0500203 if r != nil {
204 if isDirectIface(t) {
205 writebarrierptr((*uintptr)(r), uintptr(ip.data))
206 } else {
207 typedmemmove(t, r, ip.data)
208 }
Keith Randall483cb612014-08-07 13:58:42 -0700209 }
Russ Coxccdb5092014-12-29 16:15:05 -0500210 return true
Keith Randall483cb612014-08-07 13:58:42 -0700211}
212
Russ Coxccdb5092014-12-29 16:15:05 -0500213func assertE2T(t *_type, e interface{}, r unsafe.Pointer) {
Keith Randall483cb612014-08-07 13:58:42 -0700214 ep := (*eface)(unsafe.Pointer(&e))
215 if ep._type == nil {
216 panic(&TypeAssertionError{"", "", *t._string, ""})
217 }
218 if ep._type != t {
219 panic(&TypeAssertionError{"", *ep._type._string, *t._string, ""})
220 }
Russ Coxccdb5092014-12-29 16:15:05 -0500221 if r != nil {
222 if isDirectIface(t) {
223 writebarrierptr((*uintptr)(r), uintptr(ep.data))
224 } else {
225 typedmemmove(t, r, ep.data)
226 }
Keith Randall483cb612014-08-07 13:58:42 -0700227 }
Keith Randall483cb612014-08-07 13:58:42 -0700228}
229
Josh Bleecher Snyder25e793d2015-03-17 15:14:31 -0700230// The compiler ensures that r is non-nil.
Russ Coxccdb5092014-12-29 16:15:05 -0500231func assertE2T2(t *_type, e interface{}, r unsafe.Pointer) bool {
Keith Randall483cb612014-08-07 13:58:42 -0700232 ep := (*eface)(unsafe.Pointer(&e))
Keith Randall483cb612014-08-07 13:58:42 -0700233 if ep._type != t {
Josh Bleecher Snyder25e793d2015-03-17 15:14:31 -0700234 memclr(r, uintptr(t.size))
Russ Coxccdb5092014-12-29 16:15:05 -0500235 return false
Keith Randall483cb612014-08-07 13:58:42 -0700236 }
Josh Bleecher Snyder25e793d2015-03-17 15:14:31 -0700237 if isDirectIface(t) {
238 writebarrierptr((*uintptr)(r), uintptr(ep.data))
239 } else {
240 typedmemmove(t, r, ep.data)
Keith Randall483cb612014-08-07 13:58:42 -0700241 }
Russ Coxccdb5092014-12-29 16:15:05 -0500242 return true
Keith Randall483cb612014-08-07 13:58:42 -0700243}
244
245func convI2E(i fInterface) (r interface{}) {
246 ip := (*iface)(unsafe.Pointer(&i))
247 tab := ip.tab
248 if tab == nil {
249 return
250 }
251 rp := (*eface)(unsafe.Pointer(&r))
252 rp._type = tab._type
253 rp.data = ip.data
254 return
255}
256
Russ Coxccdb5092014-12-29 16:15:05 -0500257func assertI2E(inter *interfacetype, i fInterface, r *interface{}) {
Keith Randall483cb612014-08-07 13:58:42 -0700258 ip := (*iface)(unsafe.Pointer(&i))
259 tab := ip.tab
260 if tab == nil {
261 // explicit conversions require non-nil interface value.
262 panic(&TypeAssertionError{"", "", *inter.typ._string, ""})
263 }
Russ Coxccdb5092014-12-29 16:15:05 -0500264 rp := (*eface)(unsafe.Pointer(r))
Keith Randall483cb612014-08-07 13:58:42 -0700265 rp._type = tab._type
266 rp.data = ip.data
267 return
268}
269
Josh Bleecher Snyder25e793d2015-03-17 15:14:31 -0700270// The compiler ensures that r is non-nil.
Russ Coxccdb5092014-12-29 16:15:05 -0500271func assertI2E2(inter *interfacetype, i fInterface, r *interface{}) bool {
Keith Randall483cb612014-08-07 13:58:42 -0700272 ip := (*iface)(unsafe.Pointer(&i))
273 tab := ip.tab
274 if tab == nil {
Russ Coxccdb5092014-12-29 16:15:05 -0500275 return false
Keith Randall483cb612014-08-07 13:58:42 -0700276 }
Josh Bleecher Snyder25e793d2015-03-17 15:14:31 -0700277 rp := (*eface)(unsafe.Pointer(r))
278 rp._type = tab._type
279 rp.data = ip.data
Russ Coxccdb5092014-12-29 16:15:05 -0500280 return true
Keith Randall483cb612014-08-07 13:58:42 -0700281}
282
283func convI2I(inter *interfacetype, i fInterface) (r fInterface) {
284 ip := (*iface)(unsafe.Pointer(&i))
285 tab := ip.tab
286 if tab == nil {
287 return
288 }
289 rp := (*iface)(unsafe.Pointer(&r))
290 if tab.inter == inter {
291 rp.tab = tab
292 rp.data = ip.data
293 return
294 }
295 rp.tab = getitab(inter, tab._type, false)
296 rp.data = ip.data
297 return
298}
299
Russ Coxccdb5092014-12-29 16:15:05 -0500300func assertI2I(inter *interfacetype, i fInterface, r *fInterface) {
Keith Randall483cb612014-08-07 13:58:42 -0700301 ip := (*iface)(unsafe.Pointer(&i))
302 tab := ip.tab
303 if tab == nil {
304 // explicit conversions require non-nil interface value.
305 panic(&TypeAssertionError{"", "", *inter.typ._string, ""})
306 }
Russ Coxccdb5092014-12-29 16:15:05 -0500307 rp := (*iface)(unsafe.Pointer(r))
Keith Randall483cb612014-08-07 13:58:42 -0700308 if tab.inter == inter {
309 rp.tab = tab
310 rp.data = ip.data
311 return
312 }
313 rp.tab = getitab(inter, tab._type, false)
314 rp.data = ip.data
Keith Randall483cb612014-08-07 13:58:42 -0700315}
316
Russ Coxccdb5092014-12-29 16:15:05 -0500317func assertI2I2(inter *interfacetype, i fInterface, r *fInterface) bool {
Keith Randall483cb612014-08-07 13:58:42 -0700318 ip := (*iface)(unsafe.Pointer(&i))
319 tab := ip.tab
320 if tab == nil {
Russ Coxccdb5092014-12-29 16:15:05 -0500321 if r != nil {
322 *r = nil
323 }
324 return false
Keith Randall483cb612014-08-07 13:58:42 -0700325 }
Russ Coxccdb5092014-12-29 16:15:05 -0500326 if tab.inter != inter {
327 tab = getitab(inter, tab._type, true)
328 if tab == nil {
329 if r != nil {
330 *r = nil
331 }
332 return false
333 }
334 }
335 if r != nil {
336 rp := (*iface)(unsafe.Pointer(r))
Keith Randall483cb612014-08-07 13:58:42 -0700337 rp.tab = tab
338 rp.data = ip.data
Keith Randall483cb612014-08-07 13:58:42 -0700339 }
Russ Coxccdb5092014-12-29 16:15:05 -0500340 return true
Keith Randall483cb612014-08-07 13:58:42 -0700341}
342
Russ Coxccdb5092014-12-29 16:15:05 -0500343func assertE2I(inter *interfacetype, e interface{}, r *fInterface) {
Keith Randall483cb612014-08-07 13:58:42 -0700344 ep := (*eface)(unsafe.Pointer(&e))
345 t := ep._type
346 if t == nil {
347 // explicit conversions require non-nil interface value.
348 panic(&TypeAssertionError{"", "", *inter.typ._string, ""})
349 }
Russ Coxccdb5092014-12-29 16:15:05 -0500350 rp := (*iface)(unsafe.Pointer(r))
Keith Randall483cb612014-08-07 13:58:42 -0700351 rp.tab = getitab(inter, t, false)
352 rp.data = ep.data
Keith Randall483cb612014-08-07 13:58:42 -0700353}
354
Russ Coxccdb5092014-12-29 16:15:05 -0500355func assertE2I2(inter *interfacetype, e interface{}, r *fInterface) bool {
Keith Randall483cb612014-08-07 13:58:42 -0700356 ep := (*eface)(unsafe.Pointer(&e))
357 t := ep._type
358 if t == nil {
Russ Coxccdb5092014-12-29 16:15:05 -0500359 if r != nil {
360 *r = nil
361 }
362 return false
Keith Randall483cb612014-08-07 13:58:42 -0700363 }
364 tab := getitab(inter, t, true)
365 if tab == nil {
Russ Coxccdb5092014-12-29 16:15:05 -0500366 if r != nil {
367 *r = nil
368 }
369 return false
Keith Randall483cb612014-08-07 13:58:42 -0700370 }
Russ Coxccdb5092014-12-29 16:15:05 -0500371 if r != nil {
372 rp := (*iface)(unsafe.Pointer(r))
373 rp.tab = tab
374 rp.data = ep.data
375 }
376 return true
Keith Randall483cb612014-08-07 13:58:42 -0700377}
378
Russ Cox7a524a12014-12-22 13:27:53 -0500379//go:linkname reflect_ifaceE2I reflect.ifaceE2I
Keith Randall483cb612014-08-07 13:58:42 -0700380func reflect_ifaceE2I(inter *interfacetype, e interface{}, dst *fInterface) {
Russ Coxccdb5092014-12-29 16:15:05 -0500381 assertE2I(inter, e, dst)
Keith Randall483cb612014-08-07 13:58:42 -0700382}
383
Russ Coxccdb5092014-12-29 16:15:05 -0500384func assertE2E(inter *interfacetype, e interface{}, r *interface{}) {
Keith Randall483cb612014-08-07 13:58:42 -0700385 ep := (*eface)(unsafe.Pointer(&e))
386 if ep._type == nil {
387 // explicit conversions require non-nil interface value.
388 panic(&TypeAssertionError{"", "", *inter.typ._string, ""})
389 }
Russ Coxccdb5092014-12-29 16:15:05 -0500390 *r = e
Keith Randall483cb612014-08-07 13:58:42 -0700391}
392
Josh Bleecher Snyder25e793d2015-03-17 15:14:31 -0700393// The compiler ensures that r is non-nil.
Russ Coxccdb5092014-12-29 16:15:05 -0500394func assertE2E2(inter *interfacetype, e interface{}, r *interface{}) bool {
Keith Randall483cb612014-08-07 13:58:42 -0700395 ep := (*eface)(unsafe.Pointer(&e))
396 if ep._type == nil {
Josh Bleecher Snyder25e793d2015-03-17 15:14:31 -0700397 *r = nil
Russ Coxccdb5092014-12-29 16:15:05 -0500398 return false
Keith Randall483cb612014-08-07 13:58:42 -0700399 }
Josh Bleecher Snyder25e793d2015-03-17 15:14:31 -0700400 *r = e
Russ Coxccdb5092014-12-29 16:15:05 -0500401 return true
Keith Randall483cb612014-08-07 13:58:42 -0700402}
403
Keith Randall483cb612014-08-07 13:58:42 -0700404func ifacethash(i fInterface) uint32 {
405 ip := (*iface)(unsafe.Pointer(&i))
406 tab := ip.tab
407 if tab == nil {
408 return 0
409 }
410 return tab._type.hash
411}
412
413func efacethash(e interface{}) uint32 {
414 ep := (*eface)(unsafe.Pointer(&e))
415 t := ep._type
416 if t == nil {
417 return 0
418 }
419 return t.hash
420}
Russ Coxb53b47f2014-08-28 10:36:48 -0400421
422func iterate_itabs(fn func(*itab)) {
Russ Coxf0223202014-08-28 11:45:30 -0400423 for _, h := range &hash {
Russ Coxb53b47f2014-08-28 10:36:48 -0400424 for ; h != nil; h = h.link {
425 fn(h)
426 }
427 }
428}