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