diff options
Diffstat (limited to 'src/pkg/runtime/iface.goc')
-rw-r--r-- | src/pkg/runtime/iface.goc | 620 |
1 files changed, 620 insertions, 0 deletions
diff --git a/src/pkg/runtime/iface.goc b/src/pkg/runtime/iface.goc new file mode 100644 index 000000000..c0a17e303 --- /dev/null +++ b/src/pkg/runtime/iface.goc @@ -0,0 +1,620 @@ +// Copyright 2009 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 +#include "runtime.h" +#include "arch_GOARCH.h" +#include "type.h" +#include "typekind.h" +#include "malloc.h" +#include "../../cmd/ld/textflag.h" + +func printiface(i Iface) { + runtime·printf("(%p,%p)", i.tab, i.data); +} + +func printeface(e Eface) { + runtime·printf("(%p,%p)", e.type, e.data); +} + +static Itab* hash[1009]; +static Lock ifacelock; + +static Itab* +itab(InterfaceType *inter, Type *type, int32 canfail) +{ + int32 locked; + int32 ni; + Method *t, *et; + IMethod *i, *ei; + uint32 h; + String *iname, *ipkgPath; + Itab *m; + UncommonType *x; + Type *itype; + Eface err; + + if(inter->mhdr.len == 0) + runtime·throw("internal error - misuse of itab"); + + locked = 0; + + // easy case + x = type->x; + if(x == nil) { + if(canfail) + return nil; + iname = inter->m[0].name; + goto throw; + } + + // compiler has provided some good hash codes for us. + h = inter->hash; + h += 17 * type->hash; + // TODO(rsc): h += 23 * x->mhash ? + h %= nelem(hash); + + // look twice - once without lock, once with. + // common case will be no lock contention. + for(locked=0; locked<2; locked++) { + if(locked) + runtime·lock(&ifacelock); + for(m=runtime·atomicloadp(&hash[h]); m!=nil; m=m->link) { + if(m->inter == inter && m->type == type) { + if(m->bad) { + 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) + runtime·unlock(&ifacelock); + return m; + } + } + } + + ni = inter->mhdr.len; + m = runtime·persistentalloc(sizeof(*m) + ni*sizeof m->fun[0], 0, &mstats.other_sys); + m->inter = inter; + m->type = type; + +search: + // both inter and type 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). + i = inter->m; + ei = i + inter->mhdr.len; + t = x->m; + et = t + x->mhdr.len; + for(; i < ei; i++) { + itype = i->type; + iname = i->name; + ipkgPath = i->pkgPath; + for(;; t++) { + if(t >= et) { + if(!canfail) { + throw: + // didn't find method + runtime·newTypeAssertionError( + nil, type->string, inter->string, + iname, &err); + if(locked) + runtime·unlock(&ifacelock); + runtime·panic(err); + return nil; // not reached + } + m->bad = 1; + goto out; + } + if(t->mtyp == itype && t->name == iname && t->pkgPath == ipkgPath) + break; + } + if(m) + m->fun[i - inter->m] = t->ifn; + } + +out: + if(!locked) + runtime·panicstring("invalid itab locking"); + m->link = hash[h]; + runtime·atomicstorep(&hash[h], m); + runtime·unlock(&ifacelock); + if(m->bad) + return nil; + return m; +} + +// call the callback for every itab that is currently allocated. +void +runtime·iterate_itabs(void (*callback)(Itab*)) +{ + int32 i; + Itab *tab; + + for(i = 0; i < nelem(hash); i++) { + for(tab = hash[i]; tab != nil; tab = tab->link) { + callback(tab); + } + } +} + +static void +copyin(Type *t, void *src, void **dst) +{ + uintptr size; + void *p; + Alg *alg; + + size = t->size; + alg = t->alg; + + if(size <= sizeof(*dst)) + alg->copy(size, dst, src); + else { + p = runtime·cnew(t); + alg->copy(size, p, src); + *dst = p; + } +} + +static void +copyout(Type *t, void **src, void *dst) +{ + uintptr size; + Alg *alg; + + size = t->size; + alg = t->alg; + + if(size <= sizeof(*src)) + alg->copy(size, dst, src); + else + alg->copy(size, dst, *src); +} + +#pragma textflag NOSPLIT +func typ2Itab(t *Type, inter *InterfaceType, cache **Itab) (tab *Itab) { + tab = itab(inter, t, 0); + runtime·atomicstorep(cache, tab); +} + +#pragma textflag NOSPLIT +func convT2I(t *Type, inter *InterfaceType, cache **Itab, elem *byte) (ret Iface) { + Itab *tab; + + tab = runtime·atomicloadp(cache); + if(!tab) { + tab = itab(inter, t, 0); + runtime·atomicstorep(cache, tab); + } + ret.tab = tab; + copyin(t, elem, &ret.data); +} + +#pragma textflag NOSPLIT +func convT2E(t *Type, elem *byte) (ret Eface) { + ret.type = t; + copyin(t, elem, &ret.data); +} + +static void assertI2Tret(Type *t, Iface i, byte *ret); + +/* + * NOTE: Cannot use 'func' here, because we have to declare + * a return value, the only types we have are at least 1 byte large, + * goc2c will zero the return value, and the actual return value + * might have size 0 bytes, in which case the zeroing of the + * 1 or more bytes would be wrong. + * Using C lets us control (avoid) the initial zeroing. + */ +#pragma textflag NOSPLIT +void +runtime·assertI2T(Type *t, Iface i, GoOutput retbase) +{ + assertI2Tret(t, i, (byte*)&retbase); +} + +static void +assertI2Tret(Type *t, Iface i, byte *ret) +{ + Itab *tab; + Eface err; + + tab = i.tab; + if(tab == nil) { + runtime·newTypeAssertionError( + nil, nil, t->string, + nil, &err); + runtime·panic(err); + } + if(tab->type != t) { + runtime·newTypeAssertionError( + tab->inter->string, tab->type->string, t->string, + nil, &err); + runtime·panic(err); + } + copyout(t, &i.data, ret); +} + +#pragma textflag NOSPLIT +func assertI2T2(t *Type, i Iface) (ret byte, ...) { + bool *ok; + int32 wid; + + wid = t->size; + ok = (bool*)(&ret + wid); + + if(i.tab == nil || i.tab->type != t) { + *ok = false; + runtime·memclr(&ret, wid); + return; + } + + *ok = true; + copyout(t, &i.data, &ret); +} + +func assertI2TOK(t *Type, i Iface) (ok bool) { + ok = i.tab!=nil && i.tab->type==t; +} + +static void assertE2Tret(Type *t, Eface e, byte *ret); + +/* + * NOTE: Cannot use 'func' here. See assertI2T above. + */ +#pragma textflag NOSPLIT +void +runtime·assertE2T(Type *t, Eface e, GoOutput retbase) +{ + assertE2Tret(t, e, (byte*)&retbase); +} + +static void +assertE2Tret(Type *t, Eface e, byte *ret) +{ + Eface err; + + if(e.type == nil) { + runtime·newTypeAssertionError( + nil, nil, t->string, + nil, &err); + runtime·panic(err); + } + if(e.type != t) { + runtime·newTypeAssertionError( + nil, e.type->string, t->string, + nil, &err); + runtime·panic(err); + } + copyout(t, &e.data, ret); +} + +#pragma textflag NOSPLIT +func assertE2T2(t *Type, e Eface) (ret byte, ...) { + bool *ok; + int32 wid; + + wid = t->size; + ok = (bool*)(&ret + wid); + + if(t != e.type) { + *ok = false; + runtime·memclr(&ret, wid); + return; + } + + *ok = true; + copyout(t, &e.data, &ret); +} + +func assertE2TOK(t *Type, e Eface) (ok bool) { + ok = t==e.type; +} + +func convI2E(i Iface) (ret Eface) { + Itab *tab; + + ret.data = i.data; + if((tab = i.tab) == nil) + ret.type = nil; + else + ret.type = tab->type; +} + +func assertI2E(inter *InterfaceType, i Iface) (ret Eface) { + Itab *tab; + Eface err; + + tab = i.tab; + if(tab == nil) { + // explicit conversions require non-nil interface value. + runtime·newTypeAssertionError( + nil, nil, inter->string, + nil, &err); + runtime·panic(err); + } + ret.data = i.data; + ret.type = tab->type; +} + +func assertI2E2(inter *InterfaceType, i Iface) (ret Eface, ok bool) { + Itab *tab; + + USED(inter); + tab = i.tab; + if(tab == nil) { + ret.type = nil; + ok = 0; + } else { + ret.type = tab->type; + ok = 1; + } + ret.data = i.data; +} + +func convI2I(inter *InterfaceType, i Iface) (ret Iface) { + Itab *tab; + + ret.data = i.data; + if((tab = i.tab) == nil) + ret.tab = nil; + else if(tab->inter == inter) + ret.tab = tab; + else + ret.tab = itab(inter, tab->type, 0); +} + +void +runtime·ifaceI2I(InterfaceType *inter, Iface i, Iface *ret) +{ + Itab *tab; + Eface err; + + tab = i.tab; + if(tab == nil) { + // explicit conversions require non-nil interface value. + runtime·newTypeAssertionError( + nil, nil, inter->string, + nil, &err); + runtime·panic(err); + } + ret->data = i.data; + ret->tab = itab(inter, tab->type, 0); +} + +func assertI2I(inter *InterfaceType, i Iface) (ret Iface) { + runtime·ifaceI2I(inter, i, &ret); +} + +func assertI2I2(inter *InterfaceType, i Iface) (ret Iface, ok bool) { + Itab *tab; + + tab = i.tab; + if(tab != nil && (tab->inter == inter || (tab = itab(inter, tab->type, 1)) != nil)) { + ret.data = i.data; + ret.tab = tab; + ok = 1; + } else { + ret.data = 0; + ret.tab = 0; + ok = 0; + } +} + +void +runtime·ifaceE2I(InterfaceType *inter, Eface e, Iface *ret) +{ + Type *t; + Eface err; + + t = e.type; + if(t == nil) { + // explicit conversions require non-nil interface value. + runtime·newTypeAssertionError( + nil, nil, inter->string, + nil, &err); + runtime·panic(err); + } + ret->data = e.data; + ret->tab = itab(inter, t, 0); +} + +bool +runtime·ifaceE2I2(InterfaceType *inter, Eface e, Iface *ret) +{ + ret->tab = itab(inter, e.type, 1); + if(ret->tab == nil) + return false; + ret->data = e.data; + return true; +} + +func reflect·ifaceE2I(inter *InterfaceType, e Eface, dst *Iface) { + runtime·ifaceE2I(inter, e, dst); +} + +func assertE2I(inter *InterfaceType, e Eface) (ret Iface) { + runtime·ifaceE2I(inter, e, &ret); +} + +func assertE2I2(inter *InterfaceType, e Eface) (ret Iface, ok bool) { + if(e.type == nil) { + ok = 0; + ret.data = nil; + ret.tab = nil; + } else if((ret.tab = itab(inter, e.type, 1)) == nil) { + ok = 0; + ret.data = nil; + } else { + ok = 1; + ret.data = e.data; + } +} + +func assertE2E(inter *InterfaceType, e Eface) (ret Eface) { + Type *t; + Eface err; + + t = e.type; + if(t == nil) { + // explicit conversions require non-nil interface value. + runtime·newTypeAssertionError( + nil, nil, inter->string, + nil, &err); + runtime·panic(err); + } + ret = e; +} + +func assertE2E2(inter *InterfaceType, e Eface) (ret Eface, ok bool) { + USED(inter); + ret = e; + ok = e.type != nil; +} + +static uintptr +ifacehash1(void *data, Type *t, uintptr h) +{ + Alg *alg; + uintptr size; + Eface err; + + if(t == nil) + return 0; + + alg = t->alg; + size = t->size; + if(alg->hash == runtime·nohash) { + // calling nohash will panic too, + // but we can print a better error. + runtime·newErrorString(runtime·catstring(runtime·gostringnocopy((byte*)"hash of unhashable type "), *t->string), &err); + runtime·panic(err); + } + if(size <= sizeof(data)) + alg->hash(&h, size, &data); + else + alg->hash(&h, size, data); + return h; +} + +uintptr +runtime·ifacehash(Iface a, uintptr h) +{ + if(a.tab == nil) + return h; + return ifacehash1(a.data, a.tab->type, h); +} + +uintptr +runtime·efacehash(Eface a, uintptr h) +{ + return ifacehash1(a.data, a.type, h); +} + +static bool +ifaceeq1(void *data1, void *data2, Type *t) +{ + uintptr size; + Alg *alg; + Eface err; + bool eq; + + alg = t->alg; + size = t->size; + + if(alg->equal == runtime·noequal) { + // calling noequal will panic too, + // but we can print a better error. + runtime·newErrorString(runtime·catstring(runtime·gostringnocopy((byte*)"comparing uncomparable type "), *t->string), &err); + runtime·panic(err); + } + + eq = 0; + if(size <= sizeof(data1)) + alg->equal(&eq, size, &data1, &data2); + else + alg->equal(&eq, size, data1, data2); + return eq; +} + +bool +runtime·ifaceeq_c(Iface i1, Iface i2) +{ + if(i1.tab != i2.tab) + return false; + if(i1.tab == nil) + return true; + return ifaceeq1(i1.data, i2.data, i1.tab->type); +} + +bool +runtime·efaceeq_c(Eface e1, Eface e2) +{ + if(e1.type != e2.type) + return false; + if(e1.type == nil) + return true; + return ifaceeq1(e1.data, e2.data, e1.type); +} + +func ifaceeq(i1 Iface, i2 Iface) (ret bool) { + ret = runtime·ifaceeq_c(i1, i2); +} + +func efaceeq(e1 Eface, e2 Eface) (ret bool) { + ret = runtime·efaceeq_c(e1, e2); +} + +func ifacethash(i1 Iface) (ret uint32) { + Itab *tab; + + ret = 0; + tab = i1.tab; + if(tab != nil) + ret = tab->type->hash; +} + +func efacethash(e1 Eface) (ret uint32) { + Type *t; + + ret = 0; + t = e1.type; + if(t != nil) + ret = t->hash; +} + +func reflect·unsafe_Typeof(e Eface) (ret Eface) { + if(e.type == nil) { + ret.type = nil; + ret.data = nil; + } else { + ret = *(Eface*)(e.type); + } +} + +func reflect·unsafe_New(t *Type) (ret *byte) { + ret = runtime·cnew(t); +} + +func reflect·unsafe_NewArray(t *Type, n int) (ret *byte) { + ret = runtime·cnewarray(t, n); +} + +func reflect·typelinks() (ret Slice) { + extern Type *typelink[], *etypelink[]; + static int32 first = 1; + ret.array = (byte*)typelink; + ret.len = etypelink - typelink; + ret.cap = ret.len; +} |