1
0
mirror of https://github.com/golang/go synced 2024-10-03 05:21:22 -06:00
go/src/runtime/iface.go
Michael Hudson-Doyle e1366f94ee reflect, runtime: check equality, not identity, for method names
When dynamically linking Go code, it is no longer safe to assume that
strings that end up in method names are identical if they are equal.

The performance impact seems to be noise:

benchmark                    old ns/op     new ns/op     delta
BenchmarkAssertI2E2          13.3          13.1          -1.50%
BenchmarkAssertE2I           23.5          23.2          -1.28%
BenchmarkAssertE2E2Blank     0.83          0.82          -1.20%
BenchmarkConvT2ISmall        60.7          60.1          -0.99%
BenchmarkAssertI2T           10.2          10.1          -0.98%
BenchmarkAssertE2T           10.2          10.3          +0.98%
BenchmarkConvT2ESmall        56.7          57.2          +0.88%
BenchmarkConvT2ILarge        59.4          58.9          -0.84%
BenchmarkConvI2E             13.0          12.9          -0.77%
BenchmarkAssertI2E           13.4          13.3          -0.75%
BenchmarkConvT2IUintptr      57.9          58.3          +0.69%
BenchmarkConvT2ELarge        55.9          55.6          -0.54%
BenchmarkAssertI2I           23.8          23.7          -0.42%
BenchmarkConvT2EUintptr      55.4          55.5          +0.18%
BenchmarkAssertE2E           6.12          6.11          -0.16%
BenchmarkAssertE2E2          14.4          14.4          +0.00%
BenchmarkAssertE2T2          10.0          10.0          +0.00%
BenchmarkAssertE2T2Blank     0.83          0.83          +0.00%
BenchmarkAssertE2TLarge      10.7          10.7          +0.00%
BenchmarkAssertI2E2Blank     0.83          0.83          +0.00%
BenchmarkConvI2I             23.4          23.4          +0.00%

Change-Id: I0b3dfc314215a4d4e09eec6b42c1e3ebce33eb56
Reviewed-on: https://go-review.googlesource.com/8239
Reviewed-by: Ian Lance Taylor <iant@golang.org>
Run-TryBot: Ian Lance Taylor <iant@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2015-04-11 17:35:44 +00:00

433 lines
9.5 KiB
Go

// 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 {
throw("internal error - misuse of itab")
}
// easy case
x := typ.x
if x == nil {
if canfail {
return nil
}
panic(&TypeAssertionError{"", *typ._string, *inter.typ._string, *inter.mhdr[0].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)-1)*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 := &inter.mhdr[k]
iname := i.name
ipkgpath := i.pkgpath
itype := i._type
for ; j < nt; j++ {
t := &x.mhdr[j]
if t.mtyp == itype && (t.name == iname || *t.name == *iname) && t.pkgpath == ipkgpath {
if m != nil {
*(*unsafe.Pointer)(add(unsafe.Pointer(&m.fun[0]), 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 {
throw("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, x unsafe.Pointer) (e interface{}) {
ep := (*eface)(unsafe.Pointer(&e))
if isDirectIface(t) {
ep._type = t
typedmemmove(t, unsafe.Pointer(&ep.data), elem)
} else {
if x == nil {
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.
typedmemmove(t, x, elem)
ep._type = t
ep.data = x
}
return
}
func convT2I(t *_type, inter *interfacetype, cache **itab, elem unsafe.Pointer, x 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))
}
pi := (*iface)(unsafe.Pointer(&i))
if isDirectIface(t) {
pi.tab = tab
typedmemmove(t, unsafe.Pointer(&pi.data), elem)
} else {
if x == nil {
x = newobject(t)
}
typedmemmove(t, x, elem)
pi.tab = tab
pi.data = x
}
return
}
func panicdottype(have, want, iface *_type) {
haveString := ""
if have != nil {
haveString = *have._string
}
panic(&TypeAssertionError{*iface._string, haveString, *want._string, ""})
}
func assertI2T(t *_type, i fInterface, r unsafe.Pointer) {
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, ""})
}
if r != nil {
if isDirectIface(t) {
writebarrierptr((*uintptr)(r), uintptr(ip.data))
} else {
typedmemmove(t, r, ip.data)
}
}
}
func assertI2T2(t *_type, i fInterface, r unsafe.Pointer) bool {
ip := (*iface)(unsafe.Pointer(&i))
tab := ip.tab
if tab == nil || tab._type != t {
if r != nil {
memclr(r, uintptr(t.size))
}
return false
}
if r != nil {
if isDirectIface(t) {
writebarrierptr((*uintptr)(r), uintptr(ip.data))
} else {
typedmemmove(t, r, ip.data)
}
}
return true
}
func assertE2T(t *_type, e interface{}, r unsafe.Pointer) {
ep := (*eface)(unsafe.Pointer(&e))
if ep._type == nil {
panic(&TypeAssertionError{"", "", *t._string, ""})
}
if ep._type != t {
panic(&TypeAssertionError{"", *ep._type._string, *t._string, ""})
}
if r != nil {
if isDirectIface(t) {
writebarrierptr((*uintptr)(r), uintptr(ep.data))
} else {
typedmemmove(t, r, ep.data)
}
}
}
// The compiler ensures that r is non-nil.
func assertE2T2(t *_type, e interface{}, r unsafe.Pointer) bool {
ep := (*eface)(unsafe.Pointer(&e))
if ep._type != t {
memclr(r, uintptr(t.size))
return false
}
if isDirectIface(t) {
writebarrierptr((*uintptr)(r), uintptr(ep.data))
} else {
typedmemmove(t, r, ep.data)
}
return true
}
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
}
// The compiler ensures that r is non-nil.
func assertI2E2(inter *interfacetype, i fInterface, r *interface{}) bool {
ip := (*iface)(unsafe.Pointer(&i))
tab := ip.tab
if tab == nil {
return false
}
rp := (*eface)(unsafe.Pointer(r))
rp._type = tab._type
rp.data = ip.data
return true
}
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
}
func assertI2I2(inter *interfacetype, i fInterface, r *fInterface) bool {
ip := (*iface)(unsafe.Pointer(&i))
tab := ip.tab
if tab == nil {
if r != nil {
*r = nil
}
return false
}
if tab.inter != inter {
tab = getitab(inter, tab._type, true)
if tab == nil {
if r != nil {
*r = nil
}
return false
}
}
if r != nil {
rp := (*iface)(unsafe.Pointer(r))
rp.tab = tab
rp.data = ip.data
}
return true
}
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
}
func assertE2I2(inter *interfacetype, e interface{}, r *fInterface) bool {
ep := (*eface)(unsafe.Pointer(&e))
t := ep._type
if t == nil {
if r != nil {
*r = nil
}
return false
}
tab := getitab(inter, t, true)
if tab == nil {
if r != nil {
*r = nil
}
return false
}
if r != nil {
rp := (*iface)(unsafe.Pointer(r))
rp.tab = tab
rp.data = ep.data
}
return true
}
//go:linkname reflect_ifaceE2I reflect.ifaceE2I
func reflect_ifaceE2I(inter *interfacetype, e interface{}, dst *fInterface) {
assertE2I(inter, e, dst)
}
func assertE2E(inter *interfacetype, e interface{}, r *interface{}) {
ep := (*eface)(unsafe.Pointer(&e))
if ep._type == nil {
// explicit conversions require non-nil interface value.
panic(&TypeAssertionError{"", "", *inter.typ._string, ""})
}
*r = e
}
// The compiler ensures that r is non-nil.
func assertE2E2(inter *interfacetype, e interface{}, r *interface{}) bool {
ep := (*eface)(unsafe.Pointer(&e))
if ep._type == nil {
*r = nil
return false
}
*r = e
return 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)
}
}
}