| // 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" |
| |
| extern Itab* runtime·hash[1009]; |
| extern Lock runtime·ifaceLock; |
| |
| // TODO: delete this when no longer used (ifaceE2I2 is all that's left) |
| 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->typ.hash; |
| h += 17 * type->hash; |
| // TODO(rsc): h += 23 * x->mhash ? |
| h %= nelem(runtime·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(&runtime·ifaceLock); |
| for(m=runtime·atomicloadp(&runtime·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(&runtime·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->typ.string, |
| iname, &err); |
| if(locked) |
| runtime·unlock(&runtime·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 = runtime·hash[h]; |
| runtime·atomicstorep(&runtime·hash[h], m); |
| runtime·unlock(&runtime·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(runtime·hash); i++) { |
| for(tab = runtime·hash[i]; tab != nil; tab = tab->link) { |
| callback(tab); |
| } |
| } |
| } |
| |
| // Still in C because it is called from C for finalizers. This will |
| // get converted to Go in a separate CL. This is the last user of |
| // the C version of itab(). |
| 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; |
| } |