diff options
| author | Rob Pike <r@golang.org> | 2009-06-09 09:53:44 -0700 |
|---|---|---|
| committer | Rob Pike <r@golang.org> | 2009-06-09 09:53:44 -0700 |
| commit | 7249ea4df2b4f12a4e7ed446f270cea87e4ffd34 (patch) | |
| tree | 7032a11d0cac2ae4d3e90f7a189b575b5a50f848 /src/lib/runtime/iface.c | |
| parent | acf6ef7a82b3fe61516a1bac4563706552bdf078 (diff) | |
| download | golang-7249ea4df2b4f12a4e7ed446f270cea87e4ffd34.tar.gz | |
mv src/lib to src/pkg
tests: all.bash passes, gobuild still works, godoc still works.
R=rsc
OCL=30096
CL=30102
Diffstat (limited to 'src/lib/runtime/iface.c')
| -rw-r--r-- | src/lib/runtime/iface.c | 906 |
1 files changed, 0 insertions, 906 deletions
diff --git a/src/lib/runtime/iface.c b/src/lib/runtime/iface.c deleted file mode 100644 index 6c933b1b2..000000000 --- a/src/lib/runtime/iface.c +++ /dev/null @@ -1,906 +0,0 @@ -// 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" - -int32 iface_debug = 0; - -typedef struct Sigt Sigt; -typedef struct Sigi Sigi; -typedef struct Itype Itype; - -/* - * the layout of Iface, Sigt and Sigi are known to the compiler - */ -struct Sigt -{ - byte* name; // name of basic type - Sigt* link; // for linking into hash tables - uint32 thash; // hash of type - uint32 mhash; // hash of methods - uint16 width; // width of base type in bytes - uint16 alg; // algorithm - // note: on amd64 there is a 32-bit pad here. - struct { - byte* fname; - uint32 fhash; // hash of type - uint32 offset; // offset of substruct - void (*fun)(void); - } meth[1]; // one or more - last name is nil -}; - -struct Sigi -{ - byte* name; - uint32 hash; - uint32 size; // number of methods - struct { - byte* fname; - uint32 fhash; - uint32 perm; // location of fun in Sigt - } meth[1]; // [size+1] - last name is nil -}; - -struct Itype -{ - Sigi* sigi; - Sigt* sigt; - Itype* link; - int32 bad; - int32 unused; - void (*fun[])(void); -}; - -static Iface niliface; -static Eface nileface; - -static Itype* hash[1009]; -static Lock ifacelock; - -Sigi sigi·empty[2] = { (byte*)"interface { }" }; - -static void -printsigi(Sigi *si) -{ - int32 i; - byte *name; - - sys·printpointer(si); - prints("{"); - prints((int8*)si->name); - prints(":"); - for(i=0;; i++) { - name = si->meth[i].fname; - if(name == nil) - break; - prints("["); - sys·printint(i); - prints("]\""); - prints((int8*)name); - prints("\""); - sys·printint(si->meth[i].fhash%999); - prints("/"); - sys·printint(si->meth[i].perm); - } - prints("}"); -} - -static void -printsigt(Sigt *st) -{ - int32 i; - byte *name; - - sys·printpointer(st); - prints("{"); - prints((int8*)st->name); - prints(":"); - sys·printint(st->thash%999); // type hash - prints(","); - sys·printint(st->mhash%999); // method hash - prints(","); - sys·printint(st->width); // width - prints(","); - sys·printint(st->alg); // algorithm - for(i=0;; i++) { - name = st->meth[i].fname; - if(name == nil) - break; - prints("["); - sys·printint(i); - prints("]\""); - prints((int8*)name); - prints("\""); - sys·printint(st->meth[i].fhash%999); - prints("/"); - sys·printint(st->meth[i].offset); - prints("/"); - sys·printpointer(st->meth[i].fun); - } - prints("}"); -} - -static void -printiface(Iface i) -{ - prints("("); - sys·printpointer(i.type); - prints(","); - sys·printpointer(i.data); - prints(")"); -} - -static void -printeface(Eface e) -{ - prints("("); - sys·printpointer(e.type); - prints(","); - sys·printpointer(e.data); - prints(")"); -} - -static Itype* -itype(Sigi *si, Sigt *st, int32 canfail) -{ - int32 locked; - int32 nt, ni; - uint32 ihash, h; - byte *sname, *iname; - Itype *m; - - if(si->size == 0) - throw("internal error - misuse of itype"); - - // easy case - if(st->meth[0].fname == nil) { - if(canfail) - return nil; - iname = si->meth[0].fname; - goto throw1; - } - - // compiler has provided some good hash codes for us. - h = 0; - if(si) - h += si->hash; - if(st) { - h += st->thash; - h += st->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->sigi == si && m->sigt == st) { - 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 - // give a better error. - goto throw; - } - } - if(locked) - unlock(&ifacelock); - return m; - } - } - } - - ni = si->size; - m = malloc(sizeof(*m) + ni*sizeof(m->fun[0])); - m->sigi = si; - m->sigt = st; - -throw: - nt = 0; - for(ni=0;; ni++) { - iname = si->meth[ni].fname; - if(iname == nil) - break; - - // pick up next name from - // interface signature - ihash = si->meth[ni].fhash; - - for(;; nt++) { - // pick up and compare next name - // from structure signature - sname = st->meth[nt].fname; - if(sname == nil) { - if(!canfail) { - throw1: - printf("cannot convert type %s to interface %s: missing method %s\n", - st->name, si->name, iname); - if(iface_debug) { - prints("interface"); - printsigi(si); - prints("\ntype"); - printsigt(st); - prints("\n"); - } - throw("interface conversion"); - return nil; // not reached - } - m->bad = 1; - m->link = hash[h]; - hash[h] = m; - if(locked) - unlock(&ifacelock); - return nil; - } - if(ihash == st->meth[nt].fhash && strcmp(sname, iname) == 0) - break; - } - m->fun[si->meth[ni].perm] = st->meth[nt].fun; - } - m->link = hash[h]; - hash[h] = m; - if(locked) - unlock(&ifacelock); - - return m; -} - -static void -copyin(Sigt *st, void *src, void **dst) -{ - int32 wid, alg; - void *p; - - wid = st->width; - alg = st->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(Sigt *st, void **src, void *dst) -{ - int32 wid, alg; - - wid = st->width; - alg = st->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 any); -#pragma textflag 7 -void -sys·ifaceT2I(Sigi *si, Sigt *st, ...) -{ - byte *elem; - Iface *ret; - int32 wid; - - elem = (byte*)(&st+1); - wid = st->width; - ret = (Iface*)(elem + rnd(wid, sizeof(uintptr))); - - ret->type = itype(si, st, 0); - copyin(st, elem, &ret->data); -} - -// ifaceT2E(sigt *byte, elem any) (ret any); -#pragma textflag 7 -void -sys·ifaceT2E(Sigt *st, ...) -{ - byte *elem; - Eface *ret; - int32 wid; - - elem = (byte*)(&st+1); - wid = st->width; - ret = (Eface*)(elem + rnd(wid, sizeof(uintptr))); - - ret->type = st; - copyin(st, elem, &ret->data); -} - -// ifaceI2T(sigt *byte, iface any) (ret any); -#pragma textflag 7 -void -sys·ifaceI2T(Sigt *st, Iface i, ...) -{ - Itype *im; - byte *ret; - - ret = (byte*)(&i+1); - - im = i.type; - if(im == nil) { - printf("interface is nil, not %s\n", st->name); - throw("interface conversion"); - } - if(im->sigt != st) { - printf("%s is %s, not %s\n", im->sigi->name, im->sigt->name, st->name); - throw("interface conversion"); - } - copyout(st, &i.data, ret); -} - -// ifaceI2T2(sigt *byte, iface any) (ret any, ok bool); -#pragma textflag 7 -void -sys·ifaceI2T2(Sigt *st, Iface i, ...) -{ - byte *ret; - bool *ok; - Itype *im; - int32 wid; - - ret = (byte*)(&i+1); - wid = st->width; - ok = (bool*)(ret+rnd(wid, 1)); - - im = i.type; - if(im == nil || im->sigt != st) { - *ok = false; - sys·memclr(ret, wid); - return; - } - - *ok = true; - copyout(st, &i.data, ret); -} - -// ifaceE2T(sigt *byte, iface any) (ret any); -#pragma textflag 7 -void -sys·ifaceE2T(Sigt *st, Eface e, ...) -{ - Sigt *t; - byte *ret; - - ret = (byte*)(&e+1); - - t = e.type; - if(t == nil) { - printf("interface is nil, not %s\n", st->name); - throw("interface conversion"); - } - if(t != st) { - printf("interface is %s, not %s\n", t->name, st->name); - throw("interface conversion"); - } - copyout(st, &e.data, ret); -} - -// ifaceE2T2(sigt *byte, iface any) (ret any, ok bool); -#pragma textflag 7 -void -sys·ifaceE2T2(Sigt *st, Eface e, ...) -{ - byte *ret; - bool *ok; - Sigt *t; - int32 wid; - - ret = (byte*)(&e+1); - wid = st->width; - ok = (bool*)(ret+rnd(wid, 1)); - - t = e.type; - if(t != st) { - *ok = false; - sys·memclr(ret, wid); - return; - } - - *ok = true; - copyout(st, &e.data, ret); -} - -// ifaceI2E(sigi *byte, iface any) (ret any); -// TODO(rsc): Move to back end, throw away function. -void -sys·ifaceI2E(Iface i, Eface ret) -{ - Itype *im; - - ret.data = i.data; - im = i.type; - if(im == nil) - ret.type = nil; - else - ret.type = im->sigt; - FLUSH(&ret); -} - -// ifaceI2I(sigi *byte, iface any) (ret any); -// called only for implicit (no type assertion) conversions -void -sys·ifaceI2I(Sigi *si, Iface i, Iface ret) -{ - Itype *im; - - im = i.type; - if(im == nil) { - // If incoming interface is uninitialized (zeroed) - // make the outgoing interface zeroed as well. - ret = niliface; - } else { - ret = i; - if(im->sigi != si) - ret.type = itype(si, im->sigt, 0); - } - - FLUSH(&ret); -} - -// ifaceI2Ix(sigi *byte, iface any) (ret any); -// called only for explicit conversions (with type assertion). -void -sys·ifaceI2Ix(Sigi *si, Iface i, Iface ret) -{ - Itype *im; - - im = i.type; - if(im == nil) { - // explicit conversions require non-nil interface value. - printf("interface is nil, not %s\n", si->name); - throw("interface conversion"); - } else { - ret = i; - if(im->sigi != si) - ret.type = itype(si, im->sigt, 0); - } - - FLUSH(&ret); -} - -// ifaceI2I2(sigi *byte, iface any) (ret any, ok bool); -void -sys·ifaceI2I2(Sigi *si, Iface i, Iface ret, bool ok) -{ - Itype *im; - - im = i.type; - if(im == nil) { - // If incoming interface is nil, the conversion fails. - ret = niliface; - ok = false; - } else { - ret = i; - ok = true; - if(im->sigi != si) { - ret.type = itype(si, im->sigt, 1); - if(ret.type == nil) { - ret = niliface; - ok = false; - } - } - } - - FLUSH(&ret); - FLUSH(&ok); -} - -// ifaceE2I(sigi *byte, iface any) (ret any); -// Called only for explicit conversions (with type assertion). -void -sys·ifaceE2I(Sigi *si, Eface e, Iface ret) -{ - Sigt *t; - - t = e.type; - if(t == nil) { - // explicit conversions require non-nil interface value. - printf("interface is nil, not %s\n", si->name); - throw("interface conversion"); - } else { - ret.data = e.data; - ret.type = itype(si, t, 0); - } - FLUSH(&ret); -} - -// ifaceE2I2(sigi *byte, iface any) (ret any, ok bool); -void -sys·ifaceE2I2(Sigi *si, Eface e, Iface ret, bool ok) -{ - Sigt *t; - - t = e.type; - ok = true; - if(t == nil) { - // If incoming interface is nil, the conversion fails. - ret = niliface; - ok = false; - } else { - ret.data = e.data; - ret.type = itype(si, t, 1); - if(ret.type == nil) { - ret = niliface; - ok = false; - } - } - FLUSH(&ret); - FLUSH(&ok); -} - -static uintptr -ifacehash1(void *data, Sigt *sigt) -{ - int32 alg, wid; - - if(sigt == nil) - return 0; - - alg = sigt->alg; - wid = sigt->width; - if(algarray[alg].hash == nohash) { - // calling nohash will throw too, - // but we can print a better error. - printf("hash of unhashable type %s\n", sigt->name); - 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.type == nil) - return 0; - return ifacehash1(a.data, a.type->sigt); -} - -uintptr -efacehash(Eface a) -{ - return ifacehash1(a.data, a.type); -} - -static bool -ifaceeq1(void *data1, void *data2, Sigt *sigt) -{ - int32 alg, wid; - - alg = sigt->alg; - wid = sigt->width; - - if(algarray[alg].equal == noequal) { - // calling noequal will throw too, - // but we can print a better error. - printf("comparing uncomparable type %s\n", sigt->name); - 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.type != i2.type) - return false; - if(i1.type == nil) - return true; - return ifaceeq1(i1.data, i2.data, i1.type->sigt); -} - -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 -sys·ifaceeq(Iface i1, Iface i2, bool ret) -{ - ret = ifaceeq(i1, i2); - FLUSH(&ret); -} - -// efaceeq(i1 any, i2 any) (ret bool) -void -sys·efaceeq(Eface e1, Eface e2, bool ret) -{ - ret = efaceeq(e1, e2); - FLUSH(&ret); -} - -// ifacethash(i1 any) (ret uint32); -void -sys·ifacethash(Iface i1, uint32 ret) -{ - Itype *im; - Sigt *st; - - ret = 0; - im = i1.type; - if(im != nil) { - st = im->sigt; - if(st != nil) - ret = st->thash; - } - FLUSH(&ret); -} - -// efacethash(e1 any) (ret uint32) -void -sys·efacethash(Eface e1, uint32 ret) -{ - Sigt *st; - - ret = 0; - st = e1.type; - if(st != nil) - ret = st->thash; - FLUSH(&ret); -} - -void -sys·printiface(Iface i) -{ - printiface(i); -} - -void -sys·printeface(Eface e) -{ - printeface(e); -} - -void -unsafe·Reflect(Eface i, uint64 retit, String rettype, bool retindir) -{ - int32 wid; - - if(i.type == nil) { - retit = 0; - rettype = emptystring; - retindir = false; - } else { - retit = (uint64)i.data; - rettype = gostring(i.type->name); - wid = i.type->width; - retindir = wid > sizeof(i.data); - } - FLUSH(&retit); - FLUSH(&rettype); - FLUSH(&retindir); -} - -extern Sigt *gotypesigs[]; -extern int32 ngotypesigs; - - -// The reflection library can ask to unreflect on a type -// that has never been used, so we don't have a signature for it. -// For concreteness, suppose a program does -// -// type T struct{ x []int } -// var t T; -// v := reflect.NewValue(v); -// vv := v.Field(0); -// if s, ok := vv.Interface().(string) { -// print("first field is string"); -// } -// -// vv.Interface() returns the result of sys.Unreflect with -// a typestring of "[]int". If []int is not used with interfaces -// in the rest of the program, there will be no signature in gotypesigs -// for "[]int", so we have to invent one. The requirements -// on the fake signature are: -// -// (1) any interface conversion using the signature will fail -// (2) calling unsafe.Reflect() returns the args to unreflect -// (3) the right algorithm type is used, for == and map insertion -// -// (1) is ensured by the fact that we allocate a new Sigt, -// so it will necessarily be != any Sigt in gotypesigs. -// (2) is ensured by storing the type string in the signature -// and setting the width to force the correct value of the bool indir. -// (3) is ensured by sniffing the type string. -// -// Note that (1) is correct behavior: if the program had tested -// for .([]int) instead of .(string) above, then there would be a -// signature with type string "[]int" in gotypesigs, and unreflect -// wouldn't call fakesigt. - -static Sigt* fake[1009]; -static int32 nfake; - -enum -{ - SizeofInt = 4, - SizeofFloat = 4, -}; - -// Table of prefixes of names of comparable types. -static struct { - int8 *s; - int8 n; - int8 alg; - int8 w; -} cmp[] = -{ - // basic types - "int", 3+1, AMEM, SizeofInt, // +1 is NUL - "uint", 4+1, AMEM, SizeofInt, - "int8", 4+1, AMEM, 1, - "uint8", 5+1, AMEM, 1, - "int16", 5+1, AMEM, 2, - "uint16", 6+1, AMEM, 2, - "int32", 5+1, AMEM, 4, - "uint32", 6+1, AMEM, 4, - "int64", 5+1, AMEM, 8, - "uint64", 6+1, AMEM, 8, - "uintptr", 7+1, AMEM, sizeof(uintptr), - "float", 5+1, AMEM, SizeofFloat, - "float32", 7+1, AMEM, 4, - "float64", 7+1, AMEM, 8, - "bool", 4+1, AMEM, sizeof(bool), - - // string compare is special - "string", 6+1, ASTRING, sizeof(String), - - // generic types, identified by prefix - "*", 1, AMEM, sizeof(uintptr), - "chan ", 5, AMEM, sizeof(uintptr), - "func(", 5, AMEM, sizeof(uintptr), - "map[", 4, AMEM, sizeof(uintptr), -}; - -static Sigt* -fakesigt(String type, bool indir) -{ - Sigt *sigt; - uint32 h; - int32 i, locked; - - h = 0; - for(i=0; i<type.len; i++) - h = h*37 + type.str[i]; - h += indir; - h %= nelem(fake); - - for(locked=0; locked<2; locked++) { - if(locked) - lock(&ifacelock); - for(sigt = fake[h]; sigt != nil; sigt = sigt->link) { - // don't need to compare indir. - // same type string but different indir will have - // different hashes. - if(mcmp(sigt->name, type.str, type.len) == 0) - if(sigt->name[type.len] == '\0') { - if(locked) - unlock(&ifacelock); - return sigt; - } - } - } - - sigt = malloc(sizeof(*sigt)); - sigt->name = malloc(type.len + 1); - mcpy(sigt->name, type.str, type.len); - - sigt->alg = AFAKE; - sigt->width = 1; // small width - if(indir) - sigt->width = 2*sizeof(niliface.data); // big width - - // AFAKE is like ANOEQ; check whether the type - // should have a more capable algorithm. - for(i=0; i<nelem(cmp); i++) { - if(mcmp((byte*)sigt->name, (byte*)cmp[i].s, cmp[i].n) == 0) { - sigt->alg = cmp[i].alg; - sigt->width = cmp[i].w; - break; - } - } - - sigt->link = fake[h]; - fake[h] = sigt; - - unlock(&ifacelock); - return sigt; -} - -static int32 -cmpstringchars(String a, uint8 *b) -{ - int32 i; - byte c1, c2; - - for(i=0;; i++) { - c1 = 0; - if(i < a.len) - c1 = a.str[i]; - c2 = b[i]; - if(c1 < c2) - return -1; - if(c1 > c2) - return +1; - if(c1 == 0) - return 0; - } -} - -static Sigt* -findtype(String type, bool indir) -{ - int32 i, lo, hi, m; - - lo = 0; - hi = ngotypesigs; - while(lo < hi) { - m = lo + (hi - lo)/2; - i = cmpstringchars(type, gotypesigs[m]->name); - if(i == 0) - return gotypesigs[m]; - if(i < 0) - hi = m; - else - lo = m+1; - } - return fakesigt(type, indir); -} - - -void -unsafe·Unreflect(uint64 it, String type, bool indir, Eface ret) -{ - Sigt *sigt; - - ret = nileface; - - if(cmpstring(type, emptystring) == 0) - goto out; - - if(type.len > 10 && mcmp(type.str, (byte*)"interface ", 10) == 0) { - printf("unsafe.Unreflect: cannot put %S in interface\n", type); - throw("unsafe.Unreflect"); - } - - // if we think the type should be indirect - // and caller does not, play it safe, return nil. - sigt = findtype(type, indir); - if(indir != (sigt->width > sizeof(ret.data))) - goto out; - - ret.type = sigt; - ret.data = (void*)it; - -out: - FLUSH(&ret); -} - |
