| // 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; |
| } |