summaryrefslogtreecommitdiff
path: root/src/lib/runtime/iface.c
diff options
context:
space:
mode:
authorRob Pike <r@golang.org>2009-06-09 09:53:44 -0700
committerRob Pike <r@golang.org>2009-06-09 09:53:44 -0700
commit7249ea4df2b4f12a4e7ed446f270cea87e4ffd34 (patch)
tree7032a11d0cac2ae4d3e90f7a189b575b5a50f848 /src/lib/runtime/iface.c
parentacf6ef7a82b3fe61516a1bac4563706552bdf078 (diff)
downloadgolang-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.c906
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);
-}
-