// 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 #include #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; inointerface) 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; ilink) 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); }