diff options
Diffstat (limited to 'src/cmd/gc/reflect.c')
-rw-r--r-- | src/cmd/gc/reflect.c | 939 |
1 files changed, 939 insertions, 0 deletions
diff --git a/src/cmd/gc/reflect.c b/src/cmd/gc/reflect.c new file mode 100644 index 000000000..810787d30 --- /dev/null +++ b/src/cmd/gc/reflect.c @@ -0,0 +1,939 @@ +// 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 "go.h" + +/* + * runtime interface and reflection data structures + */ + +static NodeList* signatlist; +static Sym* dtypesym(Type*); +static Sym* weaktypesym(Type*); + +static int +sigcmp(Sig *a, Sig *b) +{ + int i; + + i = strcmp(a->name, b->name); + if(i != 0) + return i; + if(a->pkg == b->pkg) + return 0; + if(a->pkg == nil) + return -1; + if(b->pkg == nil) + return +1; + return strcmp(a->pkg->path->s, b->pkg->path->s); +} + +static Sig* +lsort(Sig *l, int(*f)(Sig*, Sig*)) +{ + Sig *l1, *l2, *le; + + if(l == 0 || l->link == 0) + return l; + + l1 = l; + l2 = l; + for(;;) { + l2 = l2->link; + if(l2 == 0) + break; + l2 = l2->link; + if(l2 == 0) + break; + l1 = l1->link; + } + + l2 = l1->link; + l1->link = 0; + l1 = lsort(l, f); + l2 = lsort(l2, f); + + /* set up lead element */ + if((*f)(l1, l2) < 0) { + l = l1; + l1 = l1->link; + } else { + l = l2; + l2 = l2->link; + } + le = l; + + for(;;) { + if(l1 == 0) { + while(l2) { + le->link = l2; + le = l2; + l2 = l2->link; + } + le->link = 0; + break; + } + if(l2 == 0) { + while(l1) { + le->link = l1; + le = l1; + l1 = l1->link; + } + break; + } + if((*f)(l1, l2) < 0) { + le->link = l1; + le = l1; + l1 = l1->link; + } else { + le->link = l2; + le = l2; + l2 = l2->link; + } + } + le->link = 0; + return l; +} + +/* + * f is method type, with receiver. + * return function type, receiver as first argument (or not). + */ +Type* +methodfunc(Type *f, Type *receiver) +{ + NodeList *in, *out; + Node *d; + Type *t; + + in = nil; + if(receiver) { + d = nod(ODCLFIELD, N, N); + d->type = receiver; + in = list(in, d); + } + for(t=getinargx(f)->type; t; t=t->down) { + d = nod(ODCLFIELD, N, N); + d->type = t->type; + d->isddd = t->isddd; + in = list(in, d); + } + + out = nil; + for(t=getoutargx(f)->type; t; t=t->down) { + d = nod(ODCLFIELD, N, N); + d->type = t->type; + out = list(out, d); + } + + return functype(N, in, out); +} + +/* + * return methods of non-interface type t, sorted by name. + * generates stub functions as needed. + */ +static Sig* +methods(Type *t) +{ + Type *f, *mt, *it, *this; + Sig *a, *b; + Sym *method; + Prog *oldlist; + + // named method type + mt = methtype(t); + if(mt == T) + return nil; + expandmeth(mt->sym, mt); + + // type stored in interface word + it = t; + if(it->width > widthptr) + it = ptrto(t); + + // make list of methods for t, + // generating code if necessary. + a = nil; + oldlist = nil; + for(f=mt->xmethod; f; f=f->down) { + if(f->type->etype != TFUNC) + continue; + if(f->etype != TFIELD) + fatal("methods: not field"); + method = f->sym; + if(method == nil) + continue; + + // get receiver type for this particular method. + // if pointer receiver but non-pointer t and + // this is not an embedded pointer inside a struct, + // method does not apply. + this = getthisx(f->type)->type->type; + if(isptr[this->etype] && this->type == t) + continue; + if(isptr[this->etype] && !isptr[t->etype] + && f->embedded != 2 && !isifacemethod(f->type)) + continue; + + b = mal(sizeof(*b)); + b->link = a; + a = b; + + a->name = method->name; + if(!exportname(method->name)) { + if(method->pkg == nil) + fatal("methods: missing package"); + a->pkg = method->pkg; + } + a->isym = methodsym(method, it, 1); + a->tsym = methodsym(method, t, 0); + a->type = methodfunc(f->type, t); + a->mtype = methodfunc(f->type, nil); + + if(!(a->isym->flags & SymSiggen)) { + a->isym->flags |= SymSiggen; + if(!eqtype(this, it) || this->width < types[tptr]->width) { + if(oldlist == nil) + oldlist = pc; + // Is okay to call genwrapper here always, + // but we can generate more efficient code + // using genembedtramp if all that is necessary + // is a pointer adjustment and a JMP. + if(isptr[it->etype] && isptr[this->etype] + && f->embedded && !isifacemethod(f->type)) + genembedtramp(it, f, a->isym, 1); + else + genwrapper(it, f, a->isym, 1); + } + } + + if(!(a->tsym->flags & SymSiggen)) { + a->tsym->flags |= SymSiggen; + if(!eqtype(this, t)) { + if(oldlist == nil) + oldlist = pc; + if(isptr[t->etype] && isptr[this->etype] + && f->embedded && !isifacemethod(f->type)) + genembedtramp(t, f, a->tsym, 0); + else + genwrapper(t, f, a->tsym, 0); + } + } + } + + // restore data output + if(oldlist) { + // old list ended with AEND; change to ANOP + // so that the trampolines that follow can be found. + nopout(oldlist); + + // start new data list + newplist(); + } + + return lsort(a, sigcmp); +} + +/* + * return methods of interface type t, sorted by name. + */ +static Sig* +imethods(Type *t) +{ + Sig *a, *all, *last; + Type *f; + Sym *method, *isym; + Prog *oldlist; + + all = nil; + last = nil; + oldlist = nil; + for(f=t->type; f; f=f->down) { + if(f->etype != TFIELD) + fatal("imethods: not field"); + if(f->type->etype != TFUNC || f->sym == nil) + continue; + method = f->sym; + a = mal(sizeof(*a)); + a->name = method->name; + if(!exportname(method->name)) { + if(method->pkg == nil) + fatal("imethods: missing package"); + a->pkg = method->pkg; + } + a->mtype = f->type; + a->offset = 0; + a->type = methodfunc(f->type, nil); + + if(last && sigcmp(last, a) >= 0) + fatal("sigcmp vs sortinter %s %s", last->name, a->name); + if(last == nil) + all = a; + else + last->link = a; + last = a; + + // Compiler can only refer to wrappers for + // named interface types. + if(t->sym == S) + continue; + + // NOTE(rsc): Perhaps an oversight that + // IfaceType.Method is not in the reflect data. + // Generate the method body, so that compiled + // code can refer to it. + isym = methodsym(method, t, 0); + if(!(isym->flags & SymSiggen)) { + isym->flags |= SymSiggen; + if(oldlist == nil) + oldlist = pc; + genwrapper(t, f, isym, 0); + } + } + + if(oldlist) { + // old list ended with AEND; change to ANOP + // so that the trampolines that follow can be found. + nopout(oldlist); + + // start new data list + newplist(); + } + + return all; +} + +static void +dimportpath(Pkg *p) +{ + static Pkg *gopkg; + char *nam; + Node *n; + + if(p->pathsym != S) + return; + + if(gopkg == nil) { + gopkg = mkpkg(strlit("go")); + gopkg->name = "go"; + } + nam = smprint("importpath.%s.", p->prefix); + + n = nod(ONAME, N, N); + n->sym = pkglookup(nam, gopkg); + free(nam); + n->class = PEXTERN; + n->xoffset = 0; + p->pathsym = n->sym; + + gdatastring(n, p->path); + ggloblsym(n->sym, types[TSTRING]->width, 1); +} + +static int +dgopkgpath(Sym *s, int ot, Pkg *pkg) +{ + if(pkg == nil) + return dgostringptr(s, ot, nil); + + // Emit reference to go.importpath.""., which 6l will + // rewrite using the correct import path. Every package + // that imports this one directly defines the symbol. + if(pkg == localpkg) { + static Sym *ns; + + if(ns == nil) + ns = pkglookup("importpath.\"\".", mkpkg(strlit("go"))); + return dsymptr(s, ot, ns, 0); + } + + dimportpath(pkg); + return dsymptr(s, ot, pkg->pathsym, 0); +} + +/* + * uncommonType + * ../../pkg/runtime/type.go:/uncommonType + */ +static int +dextratype(Sym *sym, int off, Type *t, int ptroff) +{ + int ot, n; + Sym *s; + Sig *a, *m; + + m = methods(t); + if(t->sym == nil && m == nil) + return off; + + // fill in *extraType pointer in header + dsymptr(sym, ptroff, sym, off); + + n = 0; + for(a=m; a; a=a->link) { + dtypesym(a->type); + n++; + } + + ot = off; + s = sym; + if(t->sym) { + ot = dgostringptr(s, ot, t->sym->name); + if(t != types[t->etype]) + ot = dgopkgpath(s, ot, t->sym->pkg); + else + ot = dgostringptr(s, ot, nil); + } else { + ot = dgostringptr(s, ot, nil); + ot = dgostringptr(s, ot, nil); + } + + // slice header + ot = dsymptr(s, ot, s, ot + widthptr + 2*4); + ot = duint32(s, ot, n); + ot = duint32(s, ot, n); + + // methods + for(a=m; a; a=a->link) { + // method + // ../../pkg/runtime/type.go:/method + ot = dgostringptr(s, ot, a->name); + ot = dgopkgpath(s, ot, a->pkg); + ot = dsymptr(s, ot, dtypesym(a->mtype), 0); + ot = dsymptr(s, ot, dtypesym(a->type), 0); + if(a->isym) + ot = dsymptr(s, ot, a->isym, 0); + else + ot = duintptr(s, ot, 0); + if(a->tsym) + ot = dsymptr(s, ot, a->tsym, 0); + else + ot = duintptr(s, ot, 0); + } + + return ot; +} + +enum { + KindBool = 1, + KindInt, + KindInt8, + KindInt16, + KindInt32, + KindInt64, + KindUint, + KindUint8, + KindUint16, + KindUint32, + KindUint64, + KindUintptr, + KindFloat32, + KindFloat64, + KindComplex64, + KindComplex128, + KindArray, + KindChan, + KindFunc, + KindInterface, + KindMap, + KindPtr, + KindSlice, + KindString, + KindStruct, + KindUnsafePointer, + + KindNoPointers = 1<<7, +}; + +static int +kinds[] = +{ + [TINT] = KindInt, + [TUINT] = KindUint, + [TINT8] = KindInt8, + [TUINT8] = KindUint8, + [TINT16] = KindInt16, + [TUINT16] = KindUint16, + [TINT32] = KindInt32, + [TUINT32] = KindUint32, + [TINT64] = KindInt64, + [TUINT64] = KindUint64, + [TUINTPTR] = KindUintptr, + [TFLOAT32] = KindFloat32, + [TFLOAT64] = KindFloat64, + [TBOOL] = KindBool, + [TSTRING] = KindString, + [TPTR32] = KindPtr, + [TPTR64] = KindPtr, + [TSTRUCT] = KindStruct, + [TINTER] = KindInterface, + [TCHAN] = KindChan, + [TMAP] = KindMap, + [TARRAY] = KindArray, + [TFUNC] = KindFunc, + [TCOMPLEX64] = KindComplex64, + [TCOMPLEX128] = KindComplex128, + [TUNSAFEPTR] = KindUnsafePointer, +}; + +static char* +structnames[] = +{ + [TINT] = "*runtime.IntType", + [TUINT] = "*runtime.UintType", + [TINT8] = "*runtime.IntType", + [TUINT8] = "*runtime.UintType", + [TINT16] = "*runtime.IntType", + [TUINT16] = "*runtime.UintType", + [TINT32] = "*runtime.IntType", + [TUINT32] = "*runtime.UintType", + [TINT64] = "*runtime.IntType", + [TUINT64] = "*runtime.UintType", + [TUINTPTR] = "*runtime.UintType", + [TCOMPLEX64] = "*runtime.ComplexType", + [TCOMPLEX128] = "*runtime.ComplexType", + [TFLOAT32] = "*runtime.FloatType", + [TFLOAT64] = "*runtime.FloatType", + [TBOOL] = "*runtime.BoolType", + [TSTRING] = "*runtime.StringType", + [TUNSAFEPTR] = "*runtime.UnsafePointerType", + + [TPTR32] = "*runtime.PtrType", + [TPTR64] = "*runtime.PtrType", + [TSTRUCT] = "*runtime.StructType", + [TINTER] = "*runtime.InterfaceType", + [TCHAN] = "*runtime.ChanType", + [TMAP] = "*runtime.MapType", + [TARRAY] = "*runtime.ArrayType", + [TFUNC] = "*runtime.FuncType", +}; + +static Sym* +typestruct(Type *t) +{ + char *name; + int et; + + et = t->etype; + if(et < 0 || et >= nelem(structnames) || (name = structnames[et]) == nil) { + fatal("typestruct %lT", t); + return nil; // silence gcc + } + + if(isslice(t)) + name = "*runtime.SliceType"; + + return pkglookup(name, typepkg); +} + +static int +haspointers(Type *t) +{ + Type *t1; + + switch(t->etype) { + case TINT: + case TUINT: + case TINT8: + case TUINT8: + case TINT16: + case TUINT16: + case TINT32: + case TUINT32: + case TINT64: + case TUINT64: + case TUINTPTR: + case TFLOAT32: + case TFLOAT64: + case TBOOL: + return 0; + case TARRAY: + if(t->bound < 0) // slice + return 1; + return haspointers(t->type); + case TSTRUCT: + for(t1=t->type; t1!=T; t1=t1->down) + if(haspointers(t1->type)) + return 1; + return 0; + case TSTRING: + case TPTR32: + case TPTR64: + case TUNSAFEPTR: + case TINTER: + case TCHAN: + case TMAP: + case TFUNC: + default: + return 1; + } +} + +/* + * commonType + * ../../pkg/runtime/type.go:/commonType + */ +static int +dcommontype(Sym *s, int ot, Type *t) +{ + int i; + Sym *sptr; + char *p; + + dowidth(t); + + sptr = nil; + if(t->sym != nil && !isptr[t->etype]) + sptr = dtypesym(ptrto(t)); + else + sptr = weaktypesym(ptrto(t)); + + // empty interface pointing at this type. + // all the references that we emit are *interface{}; + // they point here. + ot = rnd(ot, widthptr); + ot = dsymptr(s, ot, typestruct(t), 0); + ot = dsymptr(s, ot, s, 2*widthptr); + + // ../../pkg/runtime/type.go:/commonType + // actual type structure + // type commonType struct { + // size uintptr; + // hash uint32; + // alg uint8; + // align uint8; + // fieldAlign uint8; + // kind uint8; + // string *string; + // *extraType; + // ptrToThis *Type + // } + ot = duintptr(s, ot, t->width); + ot = duint32(s, ot, typehash(t)); + ot = duint8(s, ot, algtype(t)); + ot = duint8(s, ot, t->align); // align + ot = duint8(s, ot, t->align); // fieldAlign + i = kinds[t->etype]; + if(t->etype == TARRAY && t->bound < 0) + i = KindSlice; + if(!haspointers(t)) + i |= KindNoPointers; + ot = duint8(s, ot, i); // kind + longsymnames = 1; + p = smprint("%-T", t); + longsymnames = 0; + ot = dgostringptr(s, ot, p); // string + free(p); + + // skip pointer to extraType, + // which follows the rest of this type structure. + // caller will fill in if needed. + // otherwise linker will assume 0. + ot += widthptr; + + ot = dsymptr(s, ot, sptr, 0); // ptrto type + return ot; +} + +Sym* +typesym(Type *t) +{ + char *p; + Sym *s; + + p = smprint("%#-T", t); + s = pkglookup(p, typepkg); + free(p); + return s; +} + +Node* +typename(Type *t) +{ + Sym *s; + Node *n; + + if(t == T || (isptr[t->etype] && t->type == T) || isideal(t)) + fatal("typename %T", t); + s = typesym(t); + if(s->def == N) { + n = nod(ONAME, N, N); + n->sym = s; + n->type = types[TUINT8]; + n->addable = 1; + n->ullman = 1; + n->class = PEXTERN; + n->xoffset = 0; + s->def = n; + + signatlist = list(signatlist, typenod(t)); + } + + n = nod(OADDR, s->def, N); + n->type = ptrto(s->def->type); + n->addable = 1; + n->ullman = 2; + return n; +} + +static Sym* +weaktypesym(Type *t) +{ + char *p; + Sym *s; + static Pkg *weak; + + if(weak == nil) { + weak = mkpkg(strlit("weak.type")); + weak->name = "weak.type"; + weak->prefix = "weak.type"; // not weak%2etype + } + + p = smprint("%#-T", t); + s = pkglookup(p, weak); + free(p); + return s; +} + +static Sym* +dtypesym(Type *t) +{ + int ot, xt, n, isddd, dupok; + Sym *s, *s1, *s2; + Sig *a, *m; + Type *t1, *tbase, *t2; + + if(isideal(t)) + fatal("dtypesym %T", t); + + s = typesym(t); + if(s->flags & SymSiggen) + return s; + s->flags |= SymSiggen; + + // special case (look for runtime below): + // when compiling package runtime, + // emit the type structures for int, float, etc. + tbase = t; + if(isptr[t->etype] && t->sym == S && t->type->sym != S) + tbase = t->type; + dupok = tbase->sym == S; + + if(compiling_runtime && tbase == types[tbase->etype]) // int, float, etc + goto ok; + + // named types from other files are defined only by those files + if(tbase->sym && !tbase->local) + return s; + if(isforw[tbase->etype]) + return s; + +ok: + ot = 0; + xt = 0; + switch(t->etype) { + default: + ot = dcommontype(s, ot, t); + xt = ot - 2*widthptr; + break; + + case TARRAY: + if(t->bound >= 0) { + // ../../pkg/runtime/type.go:/ArrayType + s1 = dtypesym(t->type); + t2 = typ(TARRAY); + t2->type = t->type; + t2->bound = -1; // slice + s2 = dtypesym(t2); + ot = dcommontype(s, ot, t); + xt = ot - 2*widthptr; + ot = dsymptr(s, ot, s1, 0); + ot = dsymptr(s, ot, s2, 0); + ot = duintptr(s, ot, t->bound); + } else { + // ../../pkg/runtime/type.go:/SliceType + s1 = dtypesym(t->type); + ot = dcommontype(s, ot, t); + xt = ot - 2*widthptr; + ot = dsymptr(s, ot, s1, 0); + } + break; + + case TCHAN: + // ../../pkg/runtime/type.go:/ChanType + s1 = dtypesym(t->type); + ot = dcommontype(s, ot, t); + xt = ot - 2*widthptr; + ot = dsymptr(s, ot, s1, 0); + ot = duintptr(s, ot, t->chan); + break; + + case TFUNC: + for(t1=getthisx(t)->type; t1; t1=t1->down) + dtypesym(t1->type); + isddd = 0; + for(t1=getinargx(t)->type; t1; t1=t1->down) { + isddd = t1->isddd; + dtypesym(t1->type); + } + for(t1=getoutargx(t)->type; t1; t1=t1->down) + dtypesym(t1->type); + + ot = dcommontype(s, ot, t); + xt = ot - 2*widthptr; + ot = duint8(s, ot, isddd); + + // two slice headers: in and out. + ot = rnd(ot, widthptr); + ot = dsymptr(s, ot, s, ot+2*(widthptr+2*4)); + n = t->thistuple + t->intuple; + ot = duint32(s, ot, n); + ot = duint32(s, ot, n); + ot = dsymptr(s, ot, s, ot+1*(widthptr+2*4)+n*widthptr); + ot = duint32(s, ot, t->outtuple); + ot = duint32(s, ot, t->outtuple); + + // slice data + for(t1=getthisx(t)->type; t1; t1=t1->down, n++) + ot = dsymptr(s, ot, dtypesym(t1->type), 0); + for(t1=getinargx(t)->type; t1; t1=t1->down, n++) + ot = dsymptr(s, ot, dtypesym(t1->type), 0); + for(t1=getoutargx(t)->type; t1; t1=t1->down, n++) + ot = dsymptr(s, ot, dtypesym(t1->type), 0); + break; + + case TINTER: + m = imethods(t); + n = 0; + for(a=m; a; a=a->link) { + dtypesym(a->type); + n++; + } + + // ../../pkg/runtime/type.go:/InterfaceType + ot = dcommontype(s, ot, t); + xt = ot - 2*widthptr; + ot = dsymptr(s, ot, s, ot+widthptr+2*4); + ot = duint32(s, ot, n); + ot = duint32(s, ot, n); + for(a=m; a; a=a->link) { + // ../../pkg/runtime/type.go:/imethod + ot = dgostringptr(s, ot, a->name); + ot = dgopkgpath(s, ot, a->pkg); + ot = dsymptr(s, ot, dtypesym(a->type), 0); + } + break; + + case TMAP: + // ../../pkg/runtime/type.go:/MapType + s1 = dtypesym(t->down); + s2 = dtypesym(t->type); + ot = dcommontype(s, ot, t); + xt = ot - 2*widthptr; + ot = dsymptr(s, ot, s1, 0); + ot = dsymptr(s, ot, s2, 0); + break; + + case TPTR32: + case TPTR64: + if(t->type->etype == TANY) { + // ../../pkg/runtime/type.go:/UnsafePointerType + ot = dcommontype(s, ot, t); + break; + } + // ../../pkg/runtime/type.go:/PtrType + s1 = dtypesym(t->type); + ot = dcommontype(s, ot, t); + xt = ot - 2*widthptr; + ot = dsymptr(s, ot, s1, 0); + break; + + case TSTRUCT: + // ../../pkg/runtime/type.go:/StructType + // for security, only the exported fields. + n = 0; + for(t1=t->type; t1!=T; t1=t1->down) { + dtypesym(t1->type); + n++; + } + ot = dcommontype(s, ot, t); + xt = ot - 2*widthptr; + ot = dsymptr(s, ot, s, ot+widthptr+2*4); + ot = duint32(s, ot, n); + ot = duint32(s, ot, n); + for(t1=t->type; t1!=T; t1=t1->down) { + // ../../pkg/runtime/type.go:/structField + if(t1->sym && !t1->embedded) { + ot = dgostringptr(s, ot, t1->sym->name); + if(exportname(t1->sym->name)) + ot = dgostringptr(s, ot, nil); + else + ot = dgopkgpath(s, ot, t1->sym->pkg); + } else { + ot = dgostringptr(s, ot, nil); + ot = dgostringptr(s, ot, nil); + } + ot = dsymptr(s, ot, dtypesym(t1->type), 0); + ot = dgostrlitptr(s, ot, t1->note); + ot = duintptr(s, ot, t1->width); // field offset + } + break; + } + ot = dextratype(s, ot, t, xt); + ggloblsym(s, ot, dupok); + return s; +} + +void +dumptypestructs(void) +{ + int i; + NodeList *l; + Node *n; + Type *t; + Pkg *p; + + // copy types from externdcl list to signatlist + for(l=externdcl; l; l=l->next) { + n = l->n; + if(n->op != OTYPE) + continue; + signatlist = list(signatlist, n); + } + + // process signatlist + for(l=signatlist; l; l=l->next) { + n = l->n; + if(n->op != OTYPE) + continue; + t = n->type; + dtypesym(t); + if(t->sym) + dtypesym(ptrto(t)); + } + + // generate import strings for imported packages + for(i=0; i<nelem(phash); i++) + for(p=phash[i]; p; p=p->link) + if(p->direct) + dimportpath(p); + + // do basic types if compiling package runtime. + // they have to be in at least one package, + // and runtime is always loaded implicitly, + // so this is as good as any. + // another possible choice would be package main, + // but using runtime means fewer copies in .6 files. + if(compiling_runtime) { + for(i=1; i<=TBOOL; i++) + dtypesym(ptrto(types[i])); + dtypesym(ptrto(types[TSTRING])); + dtypesym(ptrto(types[TUNSAFEPTR])); + + // add paths for runtime and main, which 6l imports implicitly. + dimportpath(runtimepkg); + dimportpath(mkpkg(strlit("main"))); + } +} |