blob: da5984cebf864958bd29c4b0acfe78b8b06c8000 [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 <u.h>
#include <libc.h>
#include "go.h"
#include "y.tab.h"
static void dumpexporttype(Type *t);
// Mark n's symbol as exported
void
exportsym(Node *n)
{
if(n == N || n->sym == S)
return;
if(n->sym->flags & (SymExport|SymPackage)) {
if(n->sym->flags & SymPackage)
yyerror("export/package mismatch: %S", n->sym);
return;
}
n->sym->flags |= SymExport;
if(debug['E'])
print("export symbol %S\n", n->sym);
exportlist = list(exportlist, n);
}
int
exportname(char *s)
{
Rune r;
if((uchar)s[0] < Runeself)
return 'A' <= s[0] && s[0] <= 'Z';
chartorune(&r, s);
return isupperrune(r);
}
static int
initname(char *s)
{
return strcmp(s, "init") == 0;
}
// exportedsym reports whether a symbol will be visible
// to files that import our package.
static int
exportedsym(Sym *sym)
{
// Builtins are visible everywhere.
if(sym->pkg == builtinpkg || sym->origpkg == builtinpkg)
return 1;
return sym->pkg == localpkg && exportname(sym->name);
}
void
autoexport(Node *n, int ctxt)
{
if(n == N || n->sym == S)
return;
if((ctxt != PEXTERN && ctxt != PFUNC) || dclcontext != PEXTERN)
return;
if(n->ntype && n->ntype->op == OTFUNC && n->ntype->left) // method
return;
// -A is for cmd/gc/mkbuiltin script, so export everything
if(debug['A'] || exportname(n->sym->name) || initname(n->sym->name))
exportsym(n);
}
static void
dumppkg(Pkg *p)
{
char *suffix;
if(p == nil || p == localpkg || p->exported || p == builtinpkg)
return;
p->exported = 1;
suffix = "";
if(!p->direct)
suffix = " // indirect";
Bprint(bout, "\timport %s \"%Z\"%s\n", p->name, p->path, suffix);
}
// Look for anything we need for the inline body
static void reexportdep(Node *n);
static void
reexportdeplist(NodeList *ll)
{
for(; ll ;ll=ll->next)
reexportdep(ll->n);
}
static void
reexportdep(Node *n)
{
Type *t;
if(!n)
return;
//print("reexportdep %+hN\n", n);
switch(n->op) {
case ONAME:
switch(n->class&~PHEAP) {
case PFUNC:
// methods will be printed along with their type
// nodes for T.Method expressions
if(n->left && n->left->op == OTYPE)
break;
// nodes for method calls.
if(!n->type || n->type->thistuple > 0)
break;
// fallthrough
case PEXTERN:
if(n->sym && !exportedsym(n->sym)) {
if(debug['E'])
print("reexport name %S\n", n->sym);
exportlist = list(exportlist, n);
}
}
break;
case ODCL:
// Local variables in the bodies need their type.
t = n->left->type;
if(t != types[t->etype] && t != idealbool && t != idealstring) {
if(isptr[t->etype])
t = t->type;
if(t && t->sym && t->sym->def && !exportedsym(t->sym)) {
if(debug['E'])
print("reexport type %S from declaration\n", t->sym);
exportlist = list(exportlist, t->sym->def);
}
}
break;
case OLITERAL:
t = n->type;
if(t != types[n->type->etype] && t != idealbool && t != idealstring) {
if(isptr[t->etype])
t = t->type;
if(t && t->sym && t->sym->def && !exportedsym(t->sym)) {
if(debug['E'])
print("reexport literal type %S\n", t->sym);
exportlist = list(exportlist, t->sym->def);
}
}
// fallthrough
case OTYPE:
if(n->sym && !exportedsym(n->sym)) {
if(debug['E'])
print("reexport literal/type %S\n", n->sym);
exportlist = list(exportlist, n);
}
break;
// for operations that need a type when rendered, put the type on the export list.
case OCONV:
case OCONVIFACE:
case OCONVNOP:
case ORUNESTR:
case OARRAYBYTESTR:
case OARRAYRUNESTR:
case OSTRARRAYBYTE:
case OSTRARRAYRUNE:
case ODOTTYPE:
case ODOTTYPE2:
case OSTRUCTLIT:
case OARRAYLIT:
case OPTRLIT:
case OMAKEMAP:
case OMAKESLICE:
case OMAKECHAN:
t = n->type;
if(!t->sym && t->type)
t = t->type;
if(t && t->sym && t->sym->def && !exportedsym(t->sym)) {
if(debug['E'])
print("reexport type for expression %S\n", t->sym);
exportlist = list(exportlist, t->sym->def);
}
break;
}
reexportdep(n->left);
reexportdep(n->right);
reexportdeplist(n->list);
reexportdeplist(n->rlist);
reexportdeplist(n->ninit);
reexportdep(n->ntest);
reexportdep(n->nincr);
reexportdeplist(n->nbody);
reexportdeplist(n->nelse);
}
static void
dumpexportconst(Sym *s)
{
Node *n;
Type *t;
n = s->def;
typecheck(&n, Erv);
if(n == N || n->op != OLITERAL)
fatal("dumpexportconst: oconst nil: %S", s);
t = n->type; // may or may not be specified
dumpexporttype(t);
if(t != T && !isideal(t))
Bprint(bout, "\tconst %#S %#T = %#V\n", s, t, &n->val);
else
Bprint(bout, "\tconst %#S = %#V\n", s, &n->val);
}
static void
dumpexportvar(Sym *s)
{
Node *n;
Type *t;
n = s->def;
typecheck(&n, Erv|Ecall);
if(n == N || n->type == T) {
yyerror("variable exported but not defined: %S", s);
return;
}
t = n->type;
dumpexporttype(t);
if(t->etype == TFUNC && n->class == PFUNC) {
if (n->inl) {
// when lazily typechecking inlined bodies, some re-exported ones may not have been typechecked yet.
// currently that can leave unresolved ONONAMEs in import-dot-ed packages in the wrong package
if(debug['l'] < 2)
typecheckinl(n);
// NOTE: The space after %#S here is necessary for ld's export data parser.
Bprint(bout, "\tfunc %#S %#hT { %#H }\n", s, t, n->inl);
reexportdeplist(n->inl);
} else
Bprint(bout, "\tfunc %#S %#hT\n", s, t);
} else
Bprint(bout, "\tvar %#S %#T\n", s, t);
}
static int
methcmp(const void *va, const void *vb)
{
Type *a, *b;
a = *(Type**)va;
b = *(Type**)vb;
return strcmp(a->sym->name, b->sym->name);
}
static void
dumpexporttype(Type *t)
{
Type *f;
Type **m;
int i, n;
if(t == T)
return;
if(t->printed || t == types[t->etype] || t == bytetype || t == runetype || t == errortype)
return;
t->printed = 1;
if(t->sym != S && t->etype != TFIELD)
dumppkg(t->sym->pkg);
dumpexporttype(t->type);
dumpexporttype(t->down);
if (t->sym == S || t->etype == TFIELD)
return;
n = 0;
for(f=t->method; f!=T; f=f->down) {
dumpexporttype(f);
n++;
}
m = mal(n*sizeof m[0]);
i = 0;
for(f=t->method; f!=T; f=f->down)
m[i++] = f;
qsort(m, n, sizeof m[0], methcmp);
Bprint(bout, "\ttype %#S %#lT\n", t->sym, t);
for(i=0; i<n; i++) {
f = m[i];
if(f->nointerface)
Bprint(bout, "\t//go:nointerface\n");
if (f->type->nname && f->type->nname->inl) { // nname was set by caninl
// when lazily typechecking inlined bodies, some re-exported ones may not have been typechecked yet.
// currently that can leave unresolved ONONAMEs in import-dot-ed packages in the wrong package
if(debug['l'] < 2)
typecheckinl(f->type->nname);
Bprint(bout, "\tfunc (%#T) %#hhS %#hT { %#H }\n", getthisx(f->type)->type, f->sym, f->type, f->type->nname->inl);
reexportdeplist(f->type->nname->inl);
} else
Bprint(bout, "\tfunc (%#T) %#hhS %#hT\n", getthisx(f->type)->type, f->sym, f->type);
}
}
static void
dumpsym(Sym *s)
{
if(s->flags & SymExported)
return;
s->flags |= SymExported;
if(s->def == N) {
yyerror("unknown export symbol: %S", s);
return;
}
// print("dumpsym %O %+S\n", s->def->op, s);
dumppkg(s->pkg);
switch(s->def->op) {
default:
yyerror("unexpected export symbol: %O %S", s->def->op, s);
break;
case OLITERAL:
dumpexportconst(s);
break;
case OTYPE:
if(s->def->type->etype == TFORW)
yyerror("export of incomplete type %S", s);
else
dumpexporttype(s->def->type);
break;
case ONAME:
dumpexportvar(s);
break;
}
}
void
dumpexport(void)
{
NodeList *l;
int32 i, lno;
Pkg *p;
lno = lineno;
Bprint(bout, "\n$$\npackage %s", localpkg->name);
if(safemode)
Bprint(bout, " safe");
Bprint(bout, "\n");
for(i=0; i<nelem(phash); i++)
for(p=phash[i]; p; p=p->link)
if(p->direct)
dumppkg(p);
for(l=exportlist; l; l=l->next) {
lineno = l->n->lineno;
dumpsym(l->n->sym);
}
Bprint(bout, "\n$$\n");
lineno = lno;
}
/*
* import
*/
/*
* return the sym for ss, which should match lexical
*/
Sym*
importsym(Sym *s, int op)
{
char *pkgstr;
if(s->def != N && s->def->op != op) {
pkgstr = smprint("during import \"%Z\"", importpkg->path);
redeclare(s, pkgstr);
}
// mark the symbol so it is not reexported
if(s->def == N) {
if(exportname(s->name) || initname(s->name))
s->flags |= SymExport;
else
s->flags |= SymPackage; // package scope
}
return s;
}
/*
* return the type pkg.name, forward declaring if needed
*/
Type*
pkgtype(Sym *s)
{
Type *t;
importsym(s, OTYPE);
if(s->def == N || s->def->op != OTYPE) {
t = typ(TFORW);
t->sym = s;
s->def = typenod(t);
}
if(s->def->type == T)
yyerror("pkgtype %S", s);
return s->def->type;
}
void
importimport(Sym *s, Strlit *z)
{
// Informational: record package name
// associated with import path, for use in
// human-readable messages.
Pkg *p;
if(isbadimport(z))
errorexit();
p = mkpkg(z);
if(p->name == nil) {
p->name = s->name;
pkglookup(s->name, nil)->npkg++;
} else if(strcmp(p->name, s->name) != 0)
yyerror("conflicting names %s and %s for package \"%Z\"", p->name, s->name, p->path);
if(!incannedimport && myimportpath != nil && strcmp(z->s, myimportpath) == 0) {
yyerror("import \"%Z\": package depends on \"%Z\" (import cycle)", importpkg->path, z);
errorexit();
}
}
void
importconst(Sym *s, Type *t, Node *n)
{
Node *n1;
importsym(s, OLITERAL);
convlit(&n, t);
if(s->def != N) // TODO: check if already the same.
return;
if(n->op != OLITERAL) {
yyerror("expression must be a constant");
return;
}
if(n->sym != S) {
n1 = nod(OXXX, N, N);
*n1 = *n;
n = n1;
}
n->orig = newname(s);
n->sym = s;
declare(n, PEXTERN);
if(debug['E'])
print("import const %S\n", s);
}
void
importvar(Sym *s, Type *t)
{
Node *n;
importsym(s, ONAME);
if(s->def != N && s->def->op == ONAME) {
if(eqtype(t, s->def->type))
return;
yyerror("inconsistent definition for var %S during import\n\t%T (in \"%Z\")\n\t%T (in \"%Z\")", s, s->def->type, s->importdef->path, t, importpkg->path);
}
n = newname(s);
s->importdef = importpkg;
n->type = t;
declare(n, PEXTERN);
if(debug['E'])
print("import var %S %lT\n", s, t);
}
void
importtype(Type *pt, Type *t)
{
Node *n;
// override declaration in unsafe.go for Pointer.
// there is no way in Go code to define unsafe.Pointer
// so we have to supply it.
if(incannedimport &&
strcmp(importpkg->name, "unsafe") == 0 &&
strcmp(pt->nod->sym->name, "Pointer") == 0) {
t = types[TUNSAFEPTR];
}
if(pt->etype == TFORW) {
n = pt->nod;
copytype(pt->nod, t);
pt->nod = n; // unzero nod
pt->sym->importdef = importpkg;
pt->sym->lastlineno = parserline();
declare(n, PEXTERN);
checkwidth(pt);
} else if(!eqtype(pt->orig, t))
yyerror("inconsistent definition for type %S during import\n\t%lT (in \"%Z\")\n\t%lT (in \"%Z\")", pt->sym, pt, pt->sym->importdef->path, t, importpkg->path);
if(debug['E'])
print("import type %T %lT\n", pt, t);
}