blob: b31eb51549283da363254f66730da2487f5be86e [file] [log] [blame]
// 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 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)
{
int o;
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;
o = 0;
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;
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;
int o;
Type *f;
Sym *method, *isym;
Prog *oldlist;
all = nil;
last = nil;
o = 0;
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))
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 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);
}
return dgostringptr(s, ot, pkg->name);
}
static void
dimportpath(Pkg *p)
{
static Pkg *gopkg;
char *nam;
Node *n;
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;
gdatastring(n, p->path);
ggloblsym(n->sym, types[TSTRING]->width, 1);
}
/*
* uncommonType
* ../../pkg/runtime/type.go:/uncommonType
*/
static Sym*
dextratype(Type *t)
{
int ot, n;
char *p;
Sym *s;
Sig *a, *m;
m = methods(t);
if(t->sym == nil && m == nil)
return nil;
n = 0;
for(a=m; a; a=a->link) {
dtypesym(a->type);
n++;
}
p = smprint("_.%#T", t);
s = pkglookup(p, typepkg);
ot = 0;
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);
}
ggloblsym(s, ot, 0);
return s;
}
enum {
KindBool = 1,
KindInt,
KindInt8,
KindInt16,
KindInt32,
KindInt64,
KindUint,
KindUint8,
KindUint16,
KindUint32,
KindUint64,
KindUintptr,
KindFloat,
KindFloat32,
KindFloat64,
KindComplex,
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,
[TFLOAT] = KindFloat,
[TFLOAT32] = KindFloat32,
[TFLOAT64] = KindFloat64,
[TBOOL] = KindBool,
[TSTRING] = KindString,
[TPTR32] = KindPtr,
[TPTR64] = KindPtr,
[TSTRUCT] = KindStruct,
[TINTER] = KindInterface,
[TCHAN] = KindChan,
[TMAP] = KindMap,
[TARRAY] = KindArray,
[TFUNC] = KindFunc,
[TCOMPLEX] = KindComplex,
[TCOMPLEX64] = KindComplex64,
[TCOMPLEX128] = KindComplex128,
};
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",
[TCOMPLEX] = "*runtime.ComplexType",
[TCOMPLEX64] = "*runtime.ComplexType",
[TCOMPLEX128] = "*runtime.ComplexType",
[TFLOAT] = "*runtime.FloatType",
[TFLOAT32] = "*runtime.FloatType",
[TFLOAT64] = "*runtime.FloatType",
[TBOOL] = "*runtime.BoolType",
[TSTRING] = "*runtime.StringType",
[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";
if(isptr[et] && t->type->etype == TANY)
name = "*runtime.UnsafePointerType";
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 TFLOAT:
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 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 *s1;
char *p;
dowidth(t);
s1 = dextratype(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;
// *nameInfo;
// }
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(isptr[t->etype] && t->type->etype == TANY)
i = KindUnsafePointer;
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);
if(s1)
ot = dsymptr(s, ot, s1, 0); // extraType
else
ot = duintptr(s, ot, 0);
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*
dtypesym(Type *t)
{
int ot, n, isddd, dupok;
Sym *s, *s1, *s2;
Sig *a, *m;
Type *t1, *tbase;
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) {
if(tbase == types[tbase->etype]) // int, float, etc
goto ok;
if(tbase->etype == tptr && tbase->type->etype == TANY) // unsafe.Pointer
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;
switch(t->etype) {
default:
ot = dcommontype(s, ot, t);
break;
case TARRAY:
// ../../pkg/runtime/type.go:/ArrayType
s1 = dtypesym(t->type);
ot = dcommontype(s, ot, t);
ot = dsymptr(s, ot, s1, 0);
if(t->bound < 0)
ot = duintptr(s, ot, -1);
else
ot = duintptr(s, ot, t->bound);
break;
case TCHAN:
// ../../pkg/runtime/type.go:/ChanType
s1 = dtypesym(t->type);
ot = dcommontype(s, ot, t);
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);
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);
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);
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);
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);
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;
}
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(pkglookup("Pointer", unsafepkg)->def->type));
// add paths for runtime and main, which 6l imports implicitly.
dimportpath(runtimepkg);
dimportpath(mkpkg(strlit("main")));
}
}