1
0
mirror of https://github.com/golang/go synced 2024-10-07 09:21:23 -06:00
go/src/cmd/gc/export.c

499 lines
10 KiB
C
Raw Normal View History

// 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;
exportlist = list(exportlist, n);
}
// Mark n's symbol as package-local
static void
packagesym(Node *n)
{
if(n == N || n->sym == S)
return;
if(n->sym->flags & (SymExport|SymPackage)) {
if(n->sym->flags & SymExport)
yyerror("export/package mismatch: %S", n->sym);
return;
}
n->sym->flags |= SymPackage;
exportlist = list(exportlist, n);
}
2008-09-24 15:12:21 -06:00
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;
}
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;
if(exportname(n->sym->name) || initname(n->sym->name))
exportsym(n);
else
packagesym(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
if(!n->type || n->type->thistuple > 0)
break;
// fallthrough
case PEXTERN:
if (n->sym && n->sym->pkg != localpkg && n->sym->pkg != builtinpkg)
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 && t->sym->pkg != localpkg && t->sym->pkg != builtinpkg) {
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 && t->sym->pkg != localpkg && t->sym->pkg != builtinpkg) {
// print("reexport literal type %+hN\n", t->sym->def);
exportlist = list(exportlist, t->sym->def);
}
}
// fallthrough
case OTYPE:
if (n->sym && n->sym->pkg != localpkg && n->sym->pkg != builtinpkg)
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 ODOTTYPE:
case OSTRUCTLIT:
case OPTRLIT:
t = n->type;
if(!t->sym && t->type)
t = t->type;
if (t && t->sym && t->sym->def && t->sym->pkg != localpkg && t->sym->pkg != builtinpkg) {
// print("reexport convnop %+hN\n", t->sym->def);
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);
2008-07-05 18:43:25 -06:00
if(n == N || n->type == T) {
2008-07-14 21:40:48 -06:00
yyerror("variable exported but not defined: %S", s);
2008-07-05 18:43:25 -06:00
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);
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->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);
cmd/gc, cmd/ld: struct field tracking This is an experiment in static analysis of Go programs to understand which struct fields a program might use. It is not part of the Go language specification, it must be enabled explicitly when building the toolchain, and it may be removed at any time. After building the toolchain with GOEXPERIMENT=fieldtrack, a specific field can be marked for tracking by including `go:"track"` in the field tag: package pkg type T struct { F int `go:"track"` G int // untracked } To simplify usage, only named struct types can have tracked fields, and only exported fields can be tracked. The implementation works by making each function begin with a sequence of no-op USEFIELD instructions declaring which tracked fields are accessed by a specific function. After the linker's dead code elimination removes unused functions, the fields referred to by the remaining USEFIELD instructions are the ones reported as used by the binary. The -k option to the linker specifies the fully qualified symbol name (such as my/pkg.list) of a string variable that should be initialized with the field tracking information for the program. The field tracking string is a sequence of lines, each terminated by a \n and describing a single tracked field referred to by the program. Each line is made up of one or more tab-separated fields. The first field is the name of the tracked field, fully qualified, as in "my/pkg.T.F". Subsequent fields give a shortest path of reverse references from that field to a global variable or function, corresponding to one way in which the program might reach that field. A common source of false positives in field tracking is types with large method sets, because a reference to the type descriptor carries with it references to all methods. To address this problem, the CL also introduces a comment annotation //go:nointerface that marks an upcoming method declaration as unavailable for use in satisfying interfaces, both statically and dynamically. Such a method is also invisible to package reflect. Again, all of this is disabled by default. It only turns on if you have GOEXPERIMENT=fieldtrack set during make.bash. R=iant, ken CC=golang-dev https://golang.org/cl/6749064
2012-11-01 22:17:21 -06:00
if(f->nointerface)
Bprint(bout, "\t//go:nointerface\n");
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;
2008-06-26 18:54:44 -06:00
lno = lineno;
Bprint(bout, "\n$$ // exports\n package %s", localpkg->name);
if(safemode)
Bprint(bout, " safe");
Bprint(bout, "\n");
2008-06-12 22:48:56 -06:00
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$$ // local types\n\n$$\n"); // 6l expects this. (see ld/go.c)
2008-06-26 18:54:44 -06:00
lineno = lno;
}
/*
* import
*/
2008-09-24 15:12:21 -06:00
/*
* return the sym for ss, which should match lexical
*/
2008-06-11 22:06:26 -06:00
Sym*
importsym(Sym *s, int op)
2008-06-11 22:06:26 -06:00
{
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
}
2008-06-11 22:06:26 -06:00
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\n\t%T", s, s->def->type, t);
}
n = newname(s);
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->lastlineno = parserline();
declare(n, PEXTERN);
checkwidth(pt);
} else if(!eqtype(pt->orig, t))
yyerror("inconsistent definition for type %S during import\n\t%lT\n\t%lT", pt->sym, pt, t);
if(debug['E'])
print("import type %T %lT\n", pt, t);
}