diff options
Diffstat (limited to 'src/runtime/iface.go')
-rw-r--r-- | src/runtime/iface.go | 439 |
1 files changed, 439 insertions, 0 deletions
diff --git a/src/runtime/iface.go b/src/runtime/iface.go new file mode 100644 index 000000000..f60b6a79c --- /dev/null +++ b/src/runtime/iface.go @@ -0,0 +1,439 @@ +// 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 +} |