summaryrefslogtreecommitdiff
path: root/src/cmd/gc/init.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/gc/init.c')
-rw-r--r--src/cmd/gc/init.c195
1 files changed, 195 insertions, 0 deletions
diff --git a/src/cmd/gc/init.c b/src/cmd/gc/init.c
new file mode 100644
index 000000000..8818db08c
--- /dev/null
+++ b/src/cmd/gc/init.c
@@ -0,0 +1,195 @@
+// 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"
+
+/*
+ * a function named init is a special case.
+ * it is called by the initialization before
+ * main is run. to make it unique within a
+ * package and also uncallable, the name,
+ * normally "pkg.init", is altered to "pkg.init·1".
+ */
+Node*
+renameinit(Node *n)
+{
+ Sym *s;
+ static int initgen;
+
+ s = n->sym;
+ if(s == S)
+ return n;
+ if(strcmp(s->name, "init") != 0)
+ return n;
+
+ snprint(namebuf, sizeof(namebuf), "init·%d", ++initgen);
+ s = lookup(namebuf);
+ return newname(s);
+}
+
+/*
+ * hand-craft the following initialization code
+ * var initdone· uint8 (1)
+ * func init() (2)
+ * if initdone· != 0 { (3)
+ * if initdone· == 2 (4)
+ * return
+ * throw(); (5)
+ * }
+ * initdone· = 1; (6)
+ * // over all matching imported symbols
+ * <pkg>.init() (7)
+ * { <init stmts> } (8)
+ * init·<n>() // if any (9)
+ * initdone· = 2; (10)
+ * return (11)
+ * }
+ */
+static int
+anyinit(NodeList *n)
+{
+ uint32 h;
+ Sym *s;
+ NodeList *l;
+
+ // are there any interesting init statements
+ for(l=n; l; l=l->next) {
+ switch(l->n->op) {
+ case ODCLFUNC:
+ case ODCLCONST:
+ case ODCLTYPE:
+ case OEMPTY:
+ break;
+ default:
+ return 1;
+ }
+ }
+
+ // is this main
+ if(strcmp(localpkg->name, "main") == 0)
+ return 1;
+
+ // is there an explicit init function
+ snprint(namebuf, sizeof(namebuf), "init·1");
+ s = lookup(namebuf);
+ if(s->def != N)
+ return 1;
+
+ // are there any imported init functions
+ for(h=0; h<NHASH; h++)
+ for(s = hash[h]; s != S; s = s->link) {
+ if(s->name[0] != 'i' || strcmp(s->name, "init") != 0)
+ continue;
+ if(s->def == N)
+ continue;
+ return 1;
+ }
+
+ // then none
+ return 0;
+}
+
+void
+fninit(NodeList *n)
+{
+ int i;
+ Node *gatevar;
+ Node *a, *b, *fn;
+ NodeList *r;
+ uint32 h;
+ Sym *s, *initsym;
+
+ if(debug['A']) {
+ // sys.go or unsafe.go during compiler build
+ return;
+ }
+
+ n = initfix(n);
+ if(!anyinit(n))
+ return;
+
+ r = nil;
+
+ // (1)
+ snprint(namebuf, sizeof(namebuf), "initdone·");
+ gatevar = newname(lookup(namebuf));
+ addvar(gatevar, types[TUINT8], PEXTERN);
+
+ // (2)
+ maxarg = 0;
+ snprint(namebuf, sizeof(namebuf), "init");
+
+ fn = nod(ODCLFUNC, N, N);
+ initsym = lookup(namebuf);
+ fn->nname = newname(initsym);
+ fn->nname->ntype = nod(OTFUNC, N, N);
+ funchdr(fn);
+
+ // (3)
+ a = nod(OIF, N, N);
+ a->ntest = nod(ONE, gatevar, nodintconst(0));
+ r = list(r, a);
+
+ // (4)
+ b = nod(OIF, N, N);
+ b->ntest = nod(OEQ, gatevar, nodintconst(2));
+ b->nbody = list1(nod(ORETURN, N, N));
+ a->nbody = list1(b);
+
+ // (5)
+ b = syslook("throwinit", 0);
+ b = nod(OCALL, b, N);
+ a->nbody = list(a->nbody, b);
+
+ // (6)
+ a = nod(OAS, gatevar, nodintconst(1));
+ r = list(r, a);
+
+ // (7)
+ for(h=0; h<NHASH; h++)
+ for(s = hash[h]; s != S; s = s->link) {
+ if(s->name[0] != 'i' || strcmp(s->name, "init") != 0)
+ continue;
+ if(s->def == N)
+ continue;
+ if(s == initsym)
+ continue;
+
+ // could check that it is fn of no args/returns
+ a = nod(OCALL, s->def, N);
+ r = list(r, a);
+ }
+
+ // (8)
+ r = concat(r, n);
+
+ // (9)
+ // could check that it is fn of no args/returns
+ for(i=1;; i++) {
+ snprint(namebuf, sizeof(namebuf), "init·%d", i);
+ s = lookup(namebuf);
+ if(s->def == N)
+ break;
+ a = nod(OCALL, s->def, N);
+ r = list(r, a);
+ }
+
+ // (10)
+ a = nod(OAS, gatevar, nodintconst(2));
+ r = list(r, a);
+
+ // (11)
+ a = nod(ORETURN, N, N);
+ r = list(r, a);
+ exportsym(fn->nname);
+
+ fn->nbody = r;
+ funcbody(fn);
+
+ curfn = fn;
+ typecheck(&fn, Etop);
+ typechecklist(r, Etop);
+ curfn = nil;
+ funccompile(fn, 0);
+}