// 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. #include "runtime.h" #include "type.h" #include "malloc.h" static void printiface(Iface i) { printf("(%p,%p)", i.tab, i.data); } static void printeface(Eface e) { printf("(%p,%p)", e.type, e.data); } /* * layout of Itab known to compilers */ struct Itab { InterfaceType* inter; Type* type; Itab* link; int32 bad; int32 unused; void (*fun[])(void); }; 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 ihash, h; String *iname; Itab *m; UncommonType *x; if(inter->mhdr.len == 0) throw("internal error - misuse of itab"); // 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) lock(&ifacelock); for(m=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) unlock(&ifacelock); return m; } } } ni = inter->mhdr.len; m = malloc(sizeof(*m) + ni*sizeof m->fun[0]); m->inter = inter; m->type = type; search: // both inter and type have method sorted by hash, // 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++) { ihash = i->hash; iname = i->name; for(;; t++) { if(t >= et) { if(!canfail) { throw: // didn't find method printf("%S is not %S: missing method %S\n", *type->string, *inter->string, *iname); throw("interface conversion"); return nil; // not reached } m->bad = 1; goto out; } if(t->hash == ihash && t->name == iname) break; } if(m) m->fun[i->perm] = t->ifn; } out: m->link = hash[h]; hash[h] = m; if(locked) unlock(&ifacelock); if(m->bad) return nil; return m; } static void copyin(Type *t, void *src, void **dst) { int32 wid, alg; void *p; wid = t->size; alg = t->alg; if(wid <= sizeof(*dst)) algarray[alg].copy(wid, dst, src); else { p = mal(wid); algarray[alg].copy(wid, p, src); *dst = p; } } static void copyout(Type *t, void **src, void *dst) { int32 wid, alg; wid = t->size; alg = t->alg; if(wid <= sizeof(*src)) algarray[alg].copy(wid, dst, src); else algarray[alg].copy(wid, dst, *src); } // ifaceT2I(sigi *byte, sigt *byte, elem any) (ret Iface); #pragma textflag 7 void runtime·ifaceT2I(InterfaceType *inter, Type *t, ...) { byte *elem; Iface *ret; int32 wid; elem = (byte*)(&t+1); wid = t->size; ret = (Iface*)(elem + rnd(wid, Structrnd)); ret->tab = itab(inter, t, 0); copyin(t, elem, &ret->data); } // ifaceT2E(sigt *byte, elem any) (ret Eface); #pragma textflag 7 void runtime·ifaceT2E(Type *t, ...) { byte *elem; Eface *ret; int32 wid; elem = (byte*)(&t+1); wid = t->size; ret = (Eface*)(elem + rnd(wid, Structrnd)); ret->type = t; copyin(t, elem, &ret->data); } // ifaceI2T(sigt *byte, iface any) (ret any); #pragma textflag 7 void runtime·ifaceI2T(Type *t, Iface i, ...) { Itab *tab; byte *ret; ret = (byte*)(&i+1); tab = i.tab; if(tab == nil) { printf("interface is nil, not %S\n", *t->string); throw("interface conversion"); } if(tab->type != t) { printf("%S is %S, not %S\n", *tab->inter->string, *tab->type->string, *t->string); throw("interface conversion"); } copyout(t, &i.data, ret); } // ifaceI2T2(sigt *byte, i Iface) (ret any, ok bool); #pragma textflag 7 void runtime·ifaceI2T2(Type *t, Iface i, ...) { byte *ret; bool *ok; int32 wid; ret = (byte*)(&i+1); wid = t->size; ok = (bool*)(ret+rnd(wid, 1)); if(i.tab == nil || i.tab->type != t) { *ok = false; runtime·memclr(ret, wid); return; } *ok = true; copyout(t, &i.data, ret); } // ifaceE2T(sigt *byte, e Eface) (ret any); #pragma textflag 7 void runtime·ifaceE2T(Type *t, Eface e, ...) { byte *ret; ret = (byte*)(&e+1); if(e.type != t) { if(e.type == nil) printf("interface is nil, not %S\n", *t->string); else printf("interface is %S, not %S\n", *e.type->string, *t->string); throw("interface conversion"); } copyout(t, &e.data, ret); } // ifaceE2T2(sigt *byte, iface any) (ret any, ok bool); #pragma textflag 7 void runtime·ifaceE2T2(Type *t, Eface e, ...) { byte *ret; bool *ok; int32 wid; ret = (byte*)(&e+1); wid = t->size; ok = (bool*)(ret+rnd(wid, 1)); if(t != e.type) { *ok = false; runtime·memclr(ret, wid); return; } *ok = true; copyout(t, &e.data, ret); } // ifaceI2E(sigi *byte, iface any) (ret any); // TODO(rsc): Move to back end, throw away function. void runtime·ifaceI2E(Iface i, Eface ret) { Itab *tab; ret.data = i.data; tab = i.tab; if(tab == nil) ret.type = nil; else ret.type = tab->type; FLUSH(&ret); } // ifaceI2I(sigi *byte, iface any) (ret any); // called only for implicit (no type assertion) conversions. // converting nil is okay. void runtime·ifaceI2I(InterfaceType *inter, Iface i, Iface ret) { Itab *tab; tab = i.tab; if(tab == nil) { // If incoming interface is uninitialized (zeroed) // make the outgoing interface zeroed as well. ret.tab = nil; ret.data = nil; } else { ret = i; if(tab->inter != inter) ret.tab = itab(inter, tab->type, 0); } FLUSH(&ret); } // ifaceI2Ix(sigi *byte, iface any) (ret any); // called only for explicit conversions (with type assertion). // converting nil is not okay. void runtime·ifaceI2Ix(InterfaceType *inter, Iface i, Iface ret) { Itab *tab; tab = i.tab; if(tab == nil) { // explicit conversions require non-nil interface value. printf("interface is nil, not %S\n", *inter->string); throw("interface conversion"); } else { ret = i; if(tab->inter != inter) ret.tab = itab(inter, tab->type, 0); } FLUSH(&ret); } // ifaceI2I2(sigi *byte, iface any) (ret any, ok bool); void runtime·ifaceI2I2(InterfaceType *inter, Iface i, Iface ret, bool ok) { Itab *tab; tab = i.tab; if(tab == nil) { // If incoming interface is nil, the conversion fails. ret.tab = nil; ret.data = nil; ok = false; } else { ret = i; ok = true; if(tab->inter != inter) { ret.tab = itab(inter, tab->type, 1); if(ret.tab == nil) { ret.data = nil; ok = false; } } } FLUSH(&ret); FLUSH(&ok); } // ifaceE2I(sigi *byte, iface any) (ret any); // Called only for explicit conversions (with type assertion). void ifaceE2I(InterfaceType *inter, Eface e, Iface *ret) { Type *t; t = e.type; if(t == nil) { // explicit conversions require non-nil interface value. printf("interface is nil, not %S\n", *inter->string); throw("interface conversion"); } else { ret->data = e.data; ret->tab = itab(inter, t, 0); } } // ifaceE2I(sigi *byte, iface any) (ret any); // Called only for explicit conversions (with type assertion). void runtime·ifaceE2I(InterfaceType *inter, Eface e, Iface ret) { ifaceE2I(inter, e, &ret); } // ifaceE2I2(sigi *byte, iface any) (ret any, ok bool); void runtime·ifaceE2I2(InterfaceType *inter, Eface e, Iface ret, bool ok) { Type *t; t = e.type; ok = true; if(t == nil) { // If incoming interface is nil, the conversion fails. ret.data = nil; ret.tab = nil; ok = false; } else { ret.data = e.data; ret.tab = itab(inter, t, 1); if(ret.tab == nil) { ret.data = nil; ok = false; } } FLUSH(&ret); FLUSH(&ok); } static uintptr ifacehash1(void *data, Type *t) { int32 alg, wid; if(t == nil) return 0; alg = t->alg; wid = t->size; if(algarray[alg].hash == nohash) { // calling nohash will throw too, // but we can print a better error. printf("hash of unhashable type %S\n", *t->string); if(alg == AFAKE) throw("fake interface hash"); throw("interface hash"); } if(wid <= sizeof(data)) return algarray[alg].hash(wid, &data); return algarray[alg].hash(wid, data); } uintptr ifacehash(Iface a) { if(a.tab == nil) return 0; return ifacehash1(a.data, a.tab->type); } uintptr efacehash(Eface a) { return ifacehash1(a.data, a.type); } static bool ifaceeq1(void *data1, void *data2, Type *t) { int32 alg, wid; alg = t->alg; wid = t->size; if(algarray[alg].equal == noequal) { // calling noequal will throw too, // but we can print a better error. printf("comparing uncomparable type %S\n", *t->string); if(alg == AFAKE) throw("fake interface compare"); throw("interface compare"); } if(wid <= sizeof(data1)) return algarray[alg].equal(wid, &data1, &data2); return algarray[alg].equal(wid, data1, data2); } bool ifaceeq(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 efaceeq(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); } // ifaceeq(i1 any, i2 any) (ret bool); void runtime·ifaceeq(Iface i1, Iface i2, bool ret) { ret = ifaceeq(i1, i2); FLUSH(&ret); } // efaceeq(i1 any, i2 any) (ret bool) void runtime·efaceeq(Eface e1, Eface e2, bool ret) { ret = efaceeq(e1, e2); FLUSH(&ret); } // ifacethash(i1 any) (ret uint32); void runtime·ifacethash(Iface i1, uint32 ret) { Itab *tab; ret = 0; tab = i1.tab; if(tab != nil) ret = tab->type->hash; FLUSH(&ret); } // efacethash(e1 any) (ret uint32) void runtime·efacethash(Eface e1, uint32 ret) { Type *t; ret = 0; t = e1.type; if(t != nil) ret = t->hash; FLUSH(&ret); } void runtime·printiface(Iface i) { printiface(i); } void runtime·printeface(Eface e) { printeface(e); } void unsafe·Typeof(Eface e, Eface ret) { if(e.type == nil) { ret.type = nil; ret.data = nil; } else ret = *(Eface*)e.type; FLUSH(&ret); } void unsafe·Reflect(Eface e, Eface rettype, void *retaddr) { uintptr *p; uintptr x; if(e.type == nil) { rettype.type = nil; rettype.data = nil; retaddr = 0; } else { rettype = *(Eface*)e.type; if(e.type->size <= sizeof(uintptr)) { // Copy data into x ... x = 0; algarray[e.type->alg].copy(e.type->size, &x, &e.data); // but then build pointer to x so that Reflect // always returns pointer to data. p = mal(sizeof(uintptr)); *p = x; } else { // Already a pointer, but still make a copy, // to preserve value semantics for interface data. p = mal(e.type->size); algarray[e.type->alg].copy(e.type->size, p, e.data); } retaddr = p; } FLUSH(&rettype); FLUSH(&retaddr); } void unsafe·Unreflect(Eface typ, void *addr, Eface e) { // Reflect library has reinterpreted typ // as its own kind of type structure. // We know that the pointer to the original // type structure sits before the data pointer. e.type = (Type*)((Eface*)typ.data-1); // Interface holds either pointer to data // or copy of original data. if(e.type->size <= sizeof(uintptr)) algarray[e.type->alg].copy(e.type->size, &e.data, addr); else { // Easier: already a pointer to data. // TODO(rsc): Should this make a copy? e.data = addr; } FLUSH(&e); } void unsafe·New(Eface typ, void *ret) { Type *t; // Reflect library has reinterpreted typ // as its own kind of type structure. // We know that the pointer to the original // type structure sits before the data pointer. t = (Type*)((Eface*)typ.data-1); if(t->kind&KindNoPointers) ret = mallocgc(t->size, RefNoPointers, 1); else ret = mal(t->size); FLUSH(&ret); } void unsafe·NewArray(Eface typ, uint32 n, void *ret) { uint64 size; Type *t; // Reflect library has reinterpreted typ // as its own kind of type structure. // We know that the pointer to the original // type structure sits before the data pointer. t = (Type*)((Eface*)typ.data-1); size = n*t->size; if(t->kind&KindNoPointers) ret = mallocgc(size, RefNoPointers, 1); else ret = mal(size); FLUSH(&ret); }