| // Copyright 2014 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 runtime |
| |
| import ( |
| "unsafe" |
| ) |
| |
| const ( |
| hashSize = 1009 |
| ) |
| |
| var ( |
| ifaceLock mutex // lock for accessing hash |
| hash [hashSize]*itab |
| ) |
| |
| // fInterface is our standard non-empty interface. We use it instead |
| // of interface{f()} in function prototypes because gofmt insists on |
| // putting lots of newlines in the otherwise concise interface{f()}. |
| type fInterface interface { |
| f() |
| } |
| |
| func getitab(inter *interfacetype, typ *_type, canfail bool) *itab { |
| if len(inter.mhdr) == 0 { |
| gothrow("internal error - misuse of itab") |
| } |
| |
| // easy case |
| x := typ.x |
| if x == nil { |
| if canfail { |
| return nil |
| } |
| i := (*imethod)(add(unsafe.Pointer(inter), unsafe.Sizeof(interfacetype{}))) |
| panic(&TypeAssertionError{"", *typ._string, *inter.typ._string, *i.name}) |
| } |
| |
| // compiler has provided some good hash codes for us. |
| h := inter.typ.hash |
| h += 17 * typ.hash |
| // TODO(rsc): h += 23 * x.mhash ? |
| h %= hashSize |
| |
| // look twice - once without lock, once with. |
| // common case will be no lock contention. |
| var m *itab |
| var locked int |
| for locked = 0; locked < 2; locked++ { |
| if locked != 0 { |
| lock(&ifaceLock) |
| } |
| for m = (*itab)(atomicloadp(unsafe.Pointer(&hash[h]))); m != nil; m = m.link { |
| if m.inter == inter && m._type == typ { |
| if m.bad != 0 { |
| m = nil |
| if !canfail { |
| // this can only happen if the conversion |
| // was already done once using the , ok form |
| // and we have a cached negative result. |
| // the cached result doesn't record which |
| // interface function was missing, so jump |
| // down to the interface check, which will |
| // do more work but give a better error. |
| goto search |
| } |
| } |
| if locked != 0 { |
| unlock(&ifaceLock) |
| } |
| return m |
| } |
| } |
| } |
| |
| m = (*itab)(persistentalloc(unsafe.Sizeof(itab{})+uintptr(len(inter.mhdr))*ptrSize, 0, &memstats.other_sys)) |
| m.inter = inter |
| m._type = typ |
| |
| search: |
| // both inter and typ have method sorted by name, |
| // and interface names are unique, |
| // so can iterate over both in lock step; |
| // the loop is O(ni+nt) not O(ni*nt). |
| ni := len(inter.mhdr) |
| nt := len(x.mhdr) |
| j := 0 |
| for k := 0; k < ni; k++ { |
| i := (*imethod)(add(unsafe.Pointer(inter), unsafe.Sizeof(interfacetype{})+uintptr(k)*unsafe.Sizeof(imethod{}))) |
| iname := i.name |
| ipkgpath := i.pkgpath |
| itype := i._type |
| for ; j < nt; j++ { |
| t := (*method)(add(unsafe.Pointer(x), unsafe.Sizeof(uncommontype{})+uintptr(j)*unsafe.Sizeof(method{}))) |
| if t.mtyp == itype && t.name == iname && t.pkgpath == ipkgpath { |
| if m != nil { |
| *(*unsafe.Pointer)(add(unsafe.Pointer(m), unsafe.Sizeof(itab{})+uintptr(k)*ptrSize)) = t.ifn |
| } |
| goto nextimethod |
| } |
| } |
| // didn't find method |
| if !canfail { |
| if locked != 0 { |
| unlock(&ifaceLock) |
| } |
| panic(&TypeAssertionError{"", *typ._string, *inter.typ._string, *iname}) |
| } |
| m.bad = 1 |
| break |
| nextimethod: |
| } |
| if locked == 0 { |
| gothrow("invalid itab locking") |
| } |
| m.link = hash[h] |
| atomicstorep(unsafe.Pointer(&hash[h]), unsafe.Pointer(m)) |
| unlock(&ifaceLock) |
| if m.bad != 0 { |
| return nil |
| } |
| return m |
| } |
| |
| func typ2Itab(t *_type, inter *interfacetype, cache **itab) *itab { |
| tab := getitab(inter, t, false) |
| atomicstorep(unsafe.Pointer(cache), unsafe.Pointer(tab)) |
| return tab |
| } |
| |
| func convT2E(t *_type, elem unsafe.Pointer) (e interface{}) { |
| size := uintptr(t.size) |
| ep := (*eface)(unsafe.Pointer(&e)) |
| if isDirectIface(t) { |
| ep._type = t |
| memmove(unsafe.Pointer(&ep.data), elem, size) |
| } else { |
| x := newobject(t) |
| // TODO: We allocate a zeroed object only to overwrite it with |
| // actual data. Figure out how to avoid zeroing. Also below in convT2I. |
| memmove(x, elem, size) |
| ep._type = t |
| ep.data = x |
| } |
| return |
| } |
| |
| func convT2I(t *_type, inter *interfacetype, cache **itab, elem unsafe.Pointer) (i fInterface) { |
| tab := (*itab)(atomicloadp(unsafe.Pointer(cache))) |
| if tab == nil { |
| tab = getitab(inter, t, false) |
| atomicstorep(unsafe.Pointer(cache), unsafe.Pointer(tab)) |
| } |
| size := uintptr(t.size) |
| pi := (*iface)(unsafe.Pointer(&i)) |
| if isDirectIface(t) { |
| pi.tab = tab |
| memmove(unsafe.Pointer(&pi.data), elem, size) |
| } else { |
| x := newobject(t) |
| memmove(x, elem, size) |
| pi.tab = tab |
| pi.data = x |
| } |
| return |
| } |
| |
| // TODO: give these routines a pointer to the result area instead of writing |
| // extra data in the outargs section. Then we can get rid of go:nosplit. |
| //go:nosplit |
| func assertI2T(t *_type, i fInterface) (r struct{}) { |
| ip := (*iface)(unsafe.Pointer(&i)) |
| tab := ip.tab |
| if tab == nil { |
| panic(&TypeAssertionError{"", "", *t._string, ""}) |
| } |
| if tab._type != t { |
| panic(&TypeAssertionError{*tab.inter.typ._string, *tab._type._string, *t._string, ""}) |
| } |
| size := uintptr(t.size) |
| if isDirectIface(t) { |
| memmove(unsafe.Pointer(&r), unsafe.Pointer(&ip.data), size) |
| } else { |
| memmove(unsafe.Pointer(&r), ip.data, size) |
| } |
| return |
| } |
| |
| //go:nosplit |
| func assertI2T2(t *_type, i fInterface) (r byte) { |
| ip := (*iface)(unsafe.Pointer(&i)) |
| size := uintptr(t.size) |
| ok := (*bool)(add(unsafe.Pointer(&r), size)) |
| tab := ip.tab |
| if tab == nil || tab._type != t { |
| *ok = false |
| memclr(unsafe.Pointer(&r), size) |
| return |
| } |
| *ok = true |
| if isDirectIface(t) { |
| memmove(unsafe.Pointer(&r), unsafe.Pointer(&ip.data), size) |
| } else { |
| memmove(unsafe.Pointer(&r), ip.data, size) |
| } |
| return |
| } |
| |
| func assertI2TOK(t *_type, i fInterface) bool { |
| ip := (*iface)(unsafe.Pointer(&i)) |
| tab := ip.tab |
| return tab != nil && tab._type == t |
| } |
| |
| //go:nosplit |
| func assertE2T(t *_type, e interface{}) (r struct{}) { |
| ep := (*eface)(unsafe.Pointer(&e)) |
| if ep._type == nil { |
| panic(&TypeAssertionError{"", "", *t._string, ""}) |
| } |
| if ep._type != t { |
| panic(&TypeAssertionError{"", *ep._type._string, *t._string, ""}) |
| } |
| size := uintptr(t.size) |
| if isDirectIface(t) { |
| memmove(unsafe.Pointer(&r), unsafe.Pointer(&ep.data), size) |
| } else { |
| memmove(unsafe.Pointer(&r), ep.data, size) |
| } |
| return |
| } |
| |
| //go:nosplit |
| func assertE2T2(t *_type, e interface{}) (r byte) { |
| ep := (*eface)(unsafe.Pointer(&e)) |
| size := uintptr(t.size) |
| ok := (*bool)(add(unsafe.Pointer(&r), size)) |
| if ep._type != t { |
| *ok = false |
| memclr(unsafe.Pointer(&r), size) |
| return |
| } |
| *ok = true |
| if isDirectIface(t) { |
| memmove(unsafe.Pointer(&r), unsafe.Pointer(&ep.data), size) |
| } else { |
| memmove(unsafe.Pointer(&r), ep.data, size) |
| } |
| return |
| } |
| |
| func assertE2TOK(t *_type, e interface{}) bool { |
| ep := (*eface)(unsafe.Pointer(&e)) |
| return t == ep._type |
| } |
| |
| func convI2E(i fInterface) (r interface{}) { |
| ip := (*iface)(unsafe.Pointer(&i)) |
| tab := ip.tab |
| if tab == nil { |
| return |
| } |
| rp := (*eface)(unsafe.Pointer(&r)) |
| rp._type = tab._type |
| rp.data = ip.data |
| return |
| } |
| |
| func assertI2E(inter *interfacetype, i fInterface) (r interface{}) { |
| ip := (*iface)(unsafe.Pointer(&i)) |
| tab := ip.tab |
| if tab == nil { |
| // explicit conversions require non-nil interface value. |
| panic(&TypeAssertionError{"", "", *inter.typ._string, ""}) |
| } |
| rp := (*eface)(unsafe.Pointer(&r)) |
| rp._type = tab._type |
| rp.data = ip.data |
| return |
| } |
| |
| func assertI2E2(inter *interfacetype, i fInterface) (r interface{}, ok bool) { |
| ip := (*iface)(unsafe.Pointer(&i)) |
| tab := ip.tab |
| if tab == nil { |
| return |
| } |
| rp := (*eface)(unsafe.Pointer(&r)) |
| rp._type = tab._type |
| rp.data = ip.data |
| ok = true |
| return |
| } |
| |
| func convI2I(inter *interfacetype, i fInterface) (r fInterface) { |
| ip := (*iface)(unsafe.Pointer(&i)) |
| tab := ip.tab |
| if tab == nil { |
| return |
| } |
| rp := (*iface)(unsafe.Pointer(&r)) |
| if tab.inter == inter { |
| rp.tab = tab |
| rp.data = ip.data |
| return |
| } |
| rp.tab = getitab(inter, tab._type, false) |
| rp.data = ip.data |
| return |
| } |
| |
| func assertI2I(inter *interfacetype, i fInterface) (r fInterface) { |
| ip := (*iface)(unsafe.Pointer(&i)) |
| tab := ip.tab |
| if tab == nil { |
| // explicit conversions require non-nil interface value. |
| panic(&TypeAssertionError{"", "", *inter.typ._string, ""}) |
| } |
| rp := (*iface)(unsafe.Pointer(&r)) |
| if tab.inter == inter { |
| rp.tab = tab |
| rp.data = ip.data |
| return |
| } |
| rp.tab = getitab(inter, tab._type, false) |
| rp.data = ip.data |
| return |
| } |
| |
| func assertI2I2(inter *interfacetype, i fInterface) (r fInterface, ok bool) { |
| ip := (*iface)(unsafe.Pointer(&i)) |
| tab := ip.tab |
| if tab == nil { |
| return |
| } |
| rp := (*iface)(unsafe.Pointer(&r)) |
| if tab.inter == inter { |
| rp.tab = tab |
| rp.data = ip.data |
| ok = true |
| return |
| } |
| tab = getitab(inter, tab._type, true) |
| if tab == nil { |
| rp.data = nil |
| rp.tab = nil |
| ok = false |
| return |
| } |
| rp.tab = tab |
| rp.data = ip.data |
| ok = true |
| return |
| } |
| |
| func assertE2I(inter *interfacetype, e interface{}) (r fInterface) { |
| ep := (*eface)(unsafe.Pointer(&e)) |
| t := ep._type |
| if t == nil { |
| // explicit conversions require non-nil interface value. |
| panic(&TypeAssertionError{"", "", *inter.typ._string, ""}) |
| } |
| rp := (*iface)(unsafe.Pointer(&r)) |
| rp.tab = getitab(inter, t, false) |
| rp.data = ep.data |
| return |
| } |
| |
| func assertE2I2(inter *interfacetype, e interface{}) (r fInterface, ok bool) { |
| ep := (*eface)(unsafe.Pointer(&e)) |
| t := ep._type |
| if t == nil { |
| return |
| } |
| tab := getitab(inter, t, true) |
| if tab == nil { |
| return |
| } |
| rp := (*iface)(unsafe.Pointer(&r)) |
| rp.tab = tab |
| rp.data = ep.data |
| ok = true |
| return |
| } |
| |
| func reflect_ifaceE2I(inter *interfacetype, e interface{}, dst *fInterface) { |
| *dst = assertE2I(inter, e) |
| } |
| |
| func assertE2E(inter *interfacetype, e interface{}) interface{} { |
| ep := (*eface)(unsafe.Pointer(&e)) |
| if ep._type == nil { |
| // explicit conversions require non-nil interface value. |
| panic(&TypeAssertionError{"", "", *inter.typ._string, ""}) |
| } |
| return e |
| } |
| |
| func assertE2E2(inter *interfacetype, e interface{}) (interface{}, bool) { |
| ep := (*eface)(unsafe.Pointer(&e)) |
| if ep._type == nil { |
| return nil, false |
| } |
| return e, true |
| } |
| |
| func ifacethash(i fInterface) uint32 { |
| ip := (*iface)(unsafe.Pointer(&i)) |
| tab := ip.tab |
| if tab == nil { |
| return 0 |
| } |
| return tab._type.hash |
| } |
| |
| func efacethash(e interface{}) uint32 { |
| ep := (*eface)(unsafe.Pointer(&e)) |
| t := ep._type |
| if t == nil { |
| return 0 |
| } |
| return t.hash |
| } |
| |
| func iterate_itabs(fn func(*itab)) { |
| for _, h := range &hash { |
| for ; h != nil; h = h.link { |
| fn(h) |
| } |
| } |
| } |
| |
| func ifaceE2I2(inter *interfacetype, e interface{}, r *fInterface) (ok bool) { |
| *r, ok = assertE2I2(inter, e) |
| return |
| } |