ww: implement package initialization
This commit is contained in:
283
cmd/w6c/cgen.c
283
cmd/w6c/cgen.c
@@ -1189,6 +1189,7 @@ ffi_collect(Cg *c, Node *file)
|
||||
if (file == NULL) return;
|
||||
for (Node *d = file->list; d; d = d->next) {
|
||||
if (d->kind != N_FNDECL) continue;
|
||||
if (d->initfn || d->initsynthetic) continue;
|
||||
for (Node *a = d->attr; a; a = a->next) {
|
||||
if (a->kind != N_ATTR) continue;
|
||||
if (strcmp(a->str, "symbol") != 0) continue;
|
||||
@@ -1487,6 +1488,7 @@ mod_collect(Cg *c, Node *file)
|
||||
use_map = u;
|
||||
continue;
|
||||
}
|
||||
if (d->initfn || d->initsynthetic) continue;
|
||||
int isfn = (d->kind == N_FNDECL);
|
||||
int track = isfn || (d->kind == N_TYPEDECL)
|
||||
|| (d->kind == N_DEF) || (d->kind == N_LET);
|
||||
@@ -4196,6 +4198,238 @@ cg_arrlit_fill_bp(Cg *c, Local **locals, Type *lu, Node *arrlit, int off)
|
||||
}
|
||||
}
|
||||
|
||||
/* Package-variable helpers publish their local only after the complete value
|
||||
* has been evaluated. Composite literals need a memory-directed path here:
|
||||
* the ordinary tuple cursor has finite register capacity, nested aggregates
|
||||
* are not scalar expressions, and an ordinary slice literal points at stack
|
||||
* storage that dies when the helper returns. This path is gated by the
|
||||
* checker-owned N_LET.initsynthetic bit and therefore cannot perturb normal
|
||||
* user locals or any statically emitted initializer. */
|
||||
static void cg_init_value_bp(Cg*, Local**, Type*, Node*, int);
|
||||
|
||||
static void
|
||||
cg_init_zero_bp(Cg *c, int off, int sz)
|
||||
{
|
||||
ins2(c, A_XORQ, areg(D_AX), areg(D_AX));
|
||||
int k = 0;
|
||||
for (; k + 8 <= sz; k += 8)
|
||||
ins2(c, A_MOVQ, areg(D_AX), amem(D_BP, off + k));
|
||||
if (k + 4 <= sz) {
|
||||
ins2(c, A_MOVL, areg(D_AX), amem(D_BP, off + k));
|
||||
k += 4;
|
||||
}
|
||||
if (k + 2 <= sz) {
|
||||
ins2(c, A_MOVW, areg(D_AX), amem(D_BP, off + k));
|
||||
k += 2;
|
||||
}
|
||||
if (k + 1 <= sz)
|
||||
ins2(c, A_MOVB, areg(D_AX), amem(D_BP, off + k));
|
||||
}
|
||||
|
||||
static void
|
||||
cg_init_copy_bp(Cg *c, int src, int dst, int sz)
|
||||
{
|
||||
ins2(c, A_LEAQ, amem(D_BP, src), areg(D_SI));
|
||||
ins2(c, A_LEAQ, amem(D_BP, dst), areg(D_BX));
|
||||
cg_aggcopy(c, sz);
|
||||
}
|
||||
|
||||
static Node *
|
||||
cg_init_strip_cast(Node *n)
|
||||
{
|
||||
while (n != NULL && n->kind == N_CAST) n = n->lhs;
|
||||
return n;
|
||||
}
|
||||
|
||||
static Tfield *
|
||||
cg_init_field(Type *u, const char *name)
|
||||
{
|
||||
for (Tfield *f = u ? u->fields : NULL; f; f = f->next)
|
||||
if (name != NULL && f->name != NULL
|
||||
&& strcmp(name, f->name) == 0)
|
||||
return f;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
cg_init_array_bp(Cg *c, Local **locals, Type *u, Node *lit, int off)
|
||||
{
|
||||
Type *et = u->sub;
|
||||
int esz = et ? (int)type_chase_named(et)->size : 1;
|
||||
int total = (int)u->alen;
|
||||
cg_init_zero_bp(c, off, (int)u->size);
|
||||
int idx = 0, lastoff = 0;
|
||||
for (Node *e = lit->list; e; e = e->next) {
|
||||
if (e->kind == N_FIELD && e->str != NULL
|
||||
&& strcmp(e->str, "...") == 0) {
|
||||
if (idx == 0)
|
||||
fatal("package initializer array repeat has no value");
|
||||
while (idx < total) {
|
||||
cg_init_copy_bp(c, lastoff, off + idx * esz, esz);
|
||||
idx++;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (idx >= total)
|
||||
fatal("package initializer array literal exceeds destination");
|
||||
lastoff = off + idx * esz;
|
||||
cg_init_value_bp(c, locals, et, e, lastoff);
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
cg_init_struct_bp(Cg *c, Local **locals, Type *u, Node *lit, int off)
|
||||
{
|
||||
cg_init_zero_bp(c, off, (int)u->size);
|
||||
for (Node *e = lit->list; e; e = e->next) {
|
||||
Tfield *f = cg_init_field(u, e->str);
|
||||
if (f == NULL) continue; /* checker owns unknown/ellipsis errors */
|
||||
cg_init_value_bp(c, locals, f->type, e->lhs,
|
||||
off + (int)f->offset);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
cg_init_tuple_bp(Cg *c, Local **locals, Type *u, Node *lit, int off)
|
||||
{
|
||||
cg_init_zero_bp(c, off, (int)u->size);
|
||||
Node *e = lit->list;
|
||||
int eoff = 0;
|
||||
for (Tparam *p = u->params; p && e; p = p->next, e = e->next) {
|
||||
cg_init_value_bp(c, locals, p->type, e, off + eoff);
|
||||
eoff += tuple_eslot(p->type);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
cg_init_slice_bp(Cg *c, Local **locals, Type *u, Node *lit, int off)
|
||||
{
|
||||
if (lit->linksym == NULL || lit->linksym[0] == '\0')
|
||||
fatal("runtime package slice literal has no canonical backing");
|
||||
Type *at = type_chase_named(lit->type);
|
||||
if (at == NULL || at->kind != TY_ARRAY || at->sub == NULL)
|
||||
fatal("runtime package slice literal has no backing type");
|
||||
int count = (int)at->alen;
|
||||
int bsz = (int)at->size;
|
||||
if (bsz != 0) {
|
||||
int scr = local_alloc(c, locals, "@initbacking", bsz, cg_frame);
|
||||
cg_init_array_bp(c, locals, at, lit, scr);
|
||||
ins2(c, A_LEAQ, amem(D_BP, scr), areg(D_SI));
|
||||
ins2(c, A_LEAQ, asym(lit->linksym), areg(D_BX));
|
||||
cg_aggcopy(c, bsz);
|
||||
}
|
||||
ins2(c, A_LEAQ, asym(lit->linksym), areg(D_AX));
|
||||
ins2(c, A_MOVQ, areg(D_AX), amem(D_BP, off));
|
||||
ins2(c, A_MOVQ, aimm(count), amem(D_BP, off + 8));
|
||||
ins2(c, A_MOVQ, aimm(count), amem(D_BP, off + 16));
|
||||
}
|
||||
|
||||
static void
|
||||
cg_init_aggregate_expr_bp(Cg *c, Local **locals, Type *t, Type *u,
|
||||
Node *expr, int off)
|
||||
{
|
||||
int sz = (int)u->size;
|
||||
if (expr->kind == N_CALL && cg_sret_retsize(t) > 0) {
|
||||
cg_sret_dest_off = off;
|
||||
cgexpr(c, expr, *locals);
|
||||
cg_sret_dest_off = 0;
|
||||
return;
|
||||
}
|
||||
if (u->kind == TY_TUPLE && cg_sret_retsize(t) == 0) {
|
||||
cgexpr(c, expr, *locals);
|
||||
int gpcur = 0, ssecur = 0, eoff = 0, f32;
|
||||
for (Tparam *p = u->params; p; p = p->next) {
|
||||
int isfloat = fld_isfloat(p->type, &f32);
|
||||
tuple_store(c, p->type, gpcur, ssecur, off + eoff);
|
||||
if (isfloat) ssecur++;
|
||||
else gpcur += tuple_eslot(p->type) / 8;
|
||||
eoff += tuple_eslot(p->type);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (expr->kind == N_CALL && u->kind == TY_STRUCT) {
|
||||
int cls[2], nb = struct_float_class(u, cls);
|
||||
if (nb > 0) {
|
||||
cgexpr(c, expr, *locals);
|
||||
int gp = 0, sse = 0;
|
||||
for (int i = 0; i < nb; i++) {
|
||||
if (cls[i]) {
|
||||
ins2(c, A_MOVSD, areg(tuple_sse_seq[sse++]),
|
||||
amem(D_BP, off + i * 8));
|
||||
} else {
|
||||
ins2(c, A_MOVQ, areg(tuple_rseq[gp++]),
|
||||
amem(D_BP, off + i * 8));
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (expr->kind == N_CALL && sz <= 24) {
|
||||
cgexpr(c, expr, *locals);
|
||||
cg_agg_reg_store(c, locals, D_BP, off, sz, 1);
|
||||
return;
|
||||
}
|
||||
if (aggarg_srcaddr(c, expr, D_SI, *locals)) {
|
||||
ins2(c, A_LEAQ, amem(D_BP, off), areg(D_BX));
|
||||
cg_aggcopy(c, sz);
|
||||
return;
|
||||
}
|
||||
fatal("package initializer aggregate expression shape unsupported");
|
||||
}
|
||||
|
||||
static void
|
||||
cg_init_value_bp(Cg *c, Local **locals, Type *t, Node *expr, int off)
|
||||
{
|
||||
Type *u = type_chase_named(t);
|
||||
Node *r = cg_init_strip_cast(expr);
|
||||
if (u == NULL || r == NULL)
|
||||
fatal("package initializer value has no type or expression");
|
||||
if (u->kind == TY_ARRAY && r->kind == N_ARRLIT) {
|
||||
cg_init_array_bp(c, locals, u, r, off);
|
||||
return;
|
||||
}
|
||||
if (u->kind == TY_STRUCT && r->kind == N_STRUCTLIT) {
|
||||
cg_init_struct_bp(c, locals, u, r, off);
|
||||
return;
|
||||
}
|
||||
if (u->kind == TY_TUPLE && r->kind == N_TUPLE) {
|
||||
cg_init_tuple_bp(c, locals, u, r, off);
|
||||
return;
|
||||
}
|
||||
if (u->kind == TY_SLICE && r->kind == N_ARRLIT) {
|
||||
cg_init_slice_bp(c, locals, u, r, off);
|
||||
return;
|
||||
}
|
||||
if (u->kind == TY_TAGGED) {
|
||||
cg_widen_tagged_store(c, locals, u, expr, D_BP, off,
|
||||
(int)u->size);
|
||||
return;
|
||||
}
|
||||
if (type_isstr(t) || u->kind == TY_SLICE) {
|
||||
cgexpr(c, expr, *locals);
|
||||
ins2(c, A_MOVQ, areg(D_AX), amem(D_BP, off));
|
||||
ins2(c, A_MOVQ, areg(D_BX), amem(D_BP, off + 8));
|
||||
ins2(c, A_MOVQ, areg(D_CX), amem(D_BP, off + 16));
|
||||
return;
|
||||
}
|
||||
if (u->kind == TY_ARRAY || u->kind == TY_STRUCT
|
||||
|| u->kind == TY_TUPLE) {
|
||||
cg_init_aggregate_expr_bp(c, locals, t, u, r, off);
|
||||
return;
|
||||
}
|
||||
cgexpr(c, expr, *locals);
|
||||
int f32 = 0;
|
||||
if (fld_isfloat(t, &f32)) {
|
||||
ins2(c, f32 ? A_MOVSS : A_MOVSD, areg(D_X0),
|
||||
amem(D_BP, off));
|
||||
return;
|
||||
}
|
||||
int sz = (int)u->size;
|
||||
if (sz != 1 && sz != 2 && sz != 4) sz = 8;
|
||||
ins2(c, fldstoreop(t, sz), areg(D_AX), amem(D_BP, off));
|
||||
}
|
||||
|
||||
/* cg_dotfield_combine — single-dot field compound combine. The old
|
||||
* field value is in BX, the rhs in AX; the result is left in AX.
|
||||
* PLUSEQ/MINUSEQ preserve the pre-#34 emission (byte-id); the other 8
|
||||
@@ -10941,7 +11175,10 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
else
|
||||
ins2(c, A_XORQ, areg(D_AX), areg(D_AX));
|
||||
}
|
||||
if (n->lhs->kind == N_IDENT) {
|
||||
if (n->lhs->kind == N_IDENT && n->lhs->refdecl != NULL
|
||||
&& n->lhs->refdecl->linksym != NULL) {
|
||||
ins1(c, A_CALL, asym(n->lhs->refdecl->linksym));
|
||||
} else if (n->lhs->kind == N_IDENT) {
|
||||
/* If the callee names a local variable holding a
|
||||
* function pointer, load it and call indirect. Without
|
||||
* this check `CALL fp(SB)` is emitted as if `fp` were
|
||||
@@ -13506,6 +13743,14 @@ cgstmt(Cg *c, Node *n, Local **locals, int *frame)
|
||||
int off = letloc->off;
|
||||
int isf = cg_isfloat(lt);
|
||||
int isf32 = type_isf32(lt);
|
||||
Node *initlit = cg_init_strip_cast(n->rhs);
|
||||
if (n->initsynthetic && initlit != NULL
|
||||
&& (initlit->kind == N_ARRLIT
|
||||
|| initlit->kind == N_STRUCTLIT
|
||||
|| initlit->kind == N_TUPLE)) {
|
||||
cg_init_value_bp(c, locals, lt, n->rhs, off);
|
||||
goto letlink;
|
||||
}
|
||||
/* alloc([], n) initialiser for a slice local: allocate
|
||||
* n*esize bytes, build the {ptr, 0, n} header in the slot.
|
||||
* Element size comes from the declared slice type.
|
||||
@@ -15843,7 +16088,9 @@ cgfn(Cg *c, FILE *out, Node *fn)
|
||||
* !sep_isdep — under sep a dep unit's main is imported==0 (path-
|
||||
* carrying `//ww:module-reset`, #57); only the root/link-entry unit
|
||||
* (wwiout==NULL, #69) keeps the bare label. */
|
||||
if (fn->str && strcmp(fn->str, "main") == 0 && !fn->imported
|
||||
if (fn->linksym != NULL)
|
||||
text->to = asym(fn->linksym);
|
||||
else if (fn->str && strcmp(fn->str, "main") == 0 && !fn->imported
|
||||
&& !c->sep_isdep)
|
||||
text->to = asym("main");
|
||||
else
|
||||
@@ -16098,6 +16345,15 @@ cgfn(Cg *c, FILE *out, Node *fn)
|
||||
fatal("#38b: >48B tagged param mixed with stack-spilled "
|
||||
"params unwired");
|
||||
|
||||
/* The package coordinator owns one complete dependency-first schedule.
|
||||
* Only the selected source/generated main invokes it; package tasks never
|
||||
* recurse through dependencies, so independent-package tie order cannot
|
||||
* accidentally become linker or DFS order. */
|
||||
if (c->init_dispatch_symbol != NULL && fn->str != NULL
|
||||
&& strcmp(fn->str, "main") == 0 && !fn->imported
|
||||
&& !c->sep_isdep)
|
||||
ins1(c, A_CALL, asym(c->init_dispatch_symbol));
|
||||
|
||||
/* Iterate the fn body's statements directly rather than dispatching
|
||||
* the outermost N_BLOCK through cgstmt — N_BLOCK now save/restores
|
||||
* the locals head to scope inner shadows, but the function body is
|
||||
@@ -17674,6 +17930,28 @@ let_pre_intern(Cg *c, Node *file)
|
||||
c->cur_source = save_source;
|
||||
}
|
||||
|
||||
static void
|
||||
emit_init_backings(FILE *out, Node *n)
|
||||
{
|
||||
for (; n; n = n->next) {
|
||||
if (n->kind == N_ARRLIT && n->linksym != NULL
|
||||
&& n->linksym[0] != '\0') {
|
||||
Type *u = type_chase_named(n->type);
|
||||
if (u == NULL || u->kind != TY_ARRAY)
|
||||
fatal("runtime package slice backing has no array type");
|
||||
emit_data_row_zero(out, "DATAW", n->linksym,
|
||||
u->size == 0 ? 1 : (int)u->size);
|
||||
}
|
||||
emit_init_backings(out, n->attr);
|
||||
emit_init_backings(out, n->lhs);
|
||||
emit_init_backings(out, n->rhs);
|
||||
emit_init_backings(out, n->cond);
|
||||
emit_init_backings(out, n->body);
|
||||
emit_init_backings(out, n->els);
|
||||
emit_init_backings(out, n->list);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
cg_file(Cg *c, FILE *out, Node *file)
|
||||
{
|
||||
@@ -17697,5 +17975,6 @@ cg_file(Cg *c, FILE *out, Node *file)
|
||||
let_pre_intern(c, file);
|
||||
emit_data(c, out);
|
||||
emit_defs(c, out, file);
|
||||
emit_init_backings(out, file->list);
|
||||
emit_lets(c, out, file);
|
||||
}
|
||||
|
||||
@@ -60,6 +60,9 @@ struct Cg {
|
||||
* the bare-`main` carve-out: a dep's `fn main` must
|
||||
* mangle on its path like any decl; only the root
|
||||
* entry stays bare. */
|
||||
const char *init_dispatch_symbol; /* package-mode entry hook; the
|
||||
* root-owned dispatcher runs the complete reachable
|
||||
* package-init schedule before source main/test main. */
|
||||
};
|
||||
|
||||
/* cgen.c */
|
||||
|
||||
262
cmd/w6c/main.c
262
cmd/w6c/main.c
@@ -1,7 +1,167 @@
|
||||
#include "gc.h"
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
struct publication {
|
||||
const char *dst;
|
||||
char *stage;
|
||||
char *backup;
|
||||
int had_old;
|
||||
int installed;
|
||||
};
|
||||
|
||||
static char *
|
||||
publish_path(const char *dst, const char *kind)
|
||||
{
|
||||
int n = snprintf(NULL, 0, "%s.w6c.%ld.%s", dst, (long)getpid(), kind);
|
||||
if (n < 0 || (size_t)n == (size_t)-1) return NULL;
|
||||
char *p = malloc((size_t)n + 1);
|
||||
if (p == NULL) return NULL;
|
||||
if (snprintf(p, (size_t)n + 1, "%s.w6c.%ld.%s", dst,
|
||||
(long)getpid(), kind) != n) {
|
||||
free(p);
|
||||
return NULL;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
static int
|
||||
materialize_stream(FILE *src, struct publication *p)
|
||||
{
|
||||
p->stage = publish_path(p->dst, "new");
|
||||
p->backup = publish_path(p->dst, "old");
|
||||
if (p->stage == NULL || p->backup == NULL) {
|
||||
fputs("w6c: out of memory\n", stderr);
|
||||
return -1;
|
||||
}
|
||||
int fd = open(p->stage, O_WRONLY | O_CREAT | O_EXCL, 0644);
|
||||
if (fd < 0) {
|
||||
fprintf(stderr, "w6c: cannot stage %s\n", p->dst);
|
||||
return -1;
|
||||
}
|
||||
FILE *out = fdopen(fd, "wb");
|
||||
if (out == NULL) {
|
||||
close(fd);
|
||||
unlink(p->stage);
|
||||
return -1;
|
||||
}
|
||||
int bad = fflush(src) != 0 || fseek(src, 0, SEEK_SET) != 0;
|
||||
unsigned char buf[65536];
|
||||
while (!bad) {
|
||||
size_t n = fread(buf, 1, sizeof buf, src);
|
||||
if (n != 0 && fwrite(buf, 1, n, out) != n) bad = 1;
|
||||
if (n < sizeof buf) {
|
||||
if (ferror(src)) bad = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (fclose(out) != 0) bad = 1;
|
||||
if (bad) {
|
||||
unlink(p->stage);
|
||||
fprintf(stderr, "w6c: cannot stage %s\n", p->dst);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
publication_free(struct publication *p)
|
||||
{
|
||||
free(p->backup);
|
||||
free(p->stage);
|
||||
}
|
||||
|
||||
/* A publication rollback name is occupied by any terminal directory entry,
|
||||
* including a dangling symlink. Never follow it while deciding whether the
|
||||
* compiler may park an existing destination there. */
|
||||
static int
|
||||
path_exists_nofollow(const char *path)
|
||||
{
|
||||
struct stat st;
|
||||
if (lstat(path, &st) == 0) return 1;
|
||||
return errno == ENOENT ? 0 : -1;
|
||||
}
|
||||
|
||||
static int
|
||||
path_is_regular_nofollow(const char *path)
|
||||
{
|
||||
struct stat st;
|
||||
return lstat(path, &st) == 0 && S_ISREG(st.st_mode);
|
||||
}
|
||||
|
||||
/* Publish assembly and interface as one rollback group. Named output bytes
|
||||
* never change before checking and lowering have both succeeded. */
|
||||
static int
|
||||
publish_all(struct publication *p, int n)
|
||||
{
|
||||
for (int i = 0; i < n; i++) {
|
||||
if (!path_is_regular_nofollow(p[i].stage)) {
|
||||
fprintf(stderr, "w6c: publication stage is not a regular file for %s\n",
|
||||
p[i].dst);
|
||||
goto rollback;
|
||||
}
|
||||
struct stat st;
|
||||
if (lstat(p[i].dst, &st) == 0) {
|
||||
if (!S_ISREG(st.st_mode)) {
|
||||
fprintf(stderr,
|
||||
"w6c: publication destination is not a regular file: %s\n",
|
||||
p[i].dst);
|
||||
goto rollback;
|
||||
}
|
||||
} else if (errno != ENOENT) {
|
||||
fprintf(stderr, "w6c: cannot inspect publication destination %s\n",
|
||||
p[i].dst);
|
||||
goto rollback;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < n; i++) {
|
||||
int exists = path_exists_nofollow(p[i].backup);
|
||||
if (exists != 0) {
|
||||
if (exists < 0)
|
||||
fprintf(stderr, "w6c: cannot inspect publication backup for %s\n",
|
||||
p[i].dst);
|
||||
else
|
||||
fprintf(stderr, "w6c: publication backup exists for %s\n",
|
||||
p[i].dst);
|
||||
goto rollback;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < n; i++) {
|
||||
if (rename(p[i].dst, p[i].backup) == 0)
|
||||
p[i].had_old = 1;
|
||||
else if (errno != ENOENT) {
|
||||
fprintf(stderr, "w6c: cannot preserve %s\n", p[i].dst);
|
||||
goto rollback;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < n; i++) {
|
||||
if (rename(p[i].stage, p[i].dst) != 0) {
|
||||
fprintf(stderr, "w6c: cannot publish %s\n", p[i].dst);
|
||||
goto rollback;
|
||||
}
|
||||
p[i].installed = 1;
|
||||
}
|
||||
/* Installation is the commit point. Backup cleanup cannot truthfully
|
||||
* turn a completely installed pair into a rejected compilation. */
|
||||
for (int i = 0; i < n; i++)
|
||||
if (p[i].had_old && unlink(p[i].backup) != 0) {
|
||||
fprintf(stderr, "w6c: cannot remove backup for %s\n", p[i].dst);
|
||||
}
|
||||
return 0;
|
||||
|
||||
rollback:
|
||||
for (int i = n - 1; i >= 0; i--) {
|
||||
if (p[i].installed) (void)unlink(p[i].dst);
|
||||
if (p[i].had_old) (void)rename(p[i].backup, p[i].dst);
|
||||
if (p[i].stage != NULL) (void)unlink(p[i].stage);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int
|
||||
slurp(const char *path, char **outbuf, u64 *outlen)
|
||||
@@ -148,8 +308,10 @@ bind_import_names(Node *list, struct importin *imports, int nimports,
|
||||
}
|
||||
if (name != NULL) {
|
||||
u->usepkgname = name;
|
||||
u->str = u->usealias ? u->usealias : name;
|
||||
u->strlen = strlen(u->str);
|
||||
if (!u->useblank) {
|
||||
u->str = u->usealias ? u->usealias : name;
|
||||
u->strlen = strlen(u->str);
|
||||
}
|
||||
} else if (!u->imported) {
|
||||
fprintf(stderr,
|
||||
"w6c: import %s has no declared package name in direct export data\n",
|
||||
@@ -159,8 +321,10 @@ bind_import_names(Node *list, struct importin *imports, int nimports,
|
||||
/* A closure-only import need not contribute declarations to this
|
||||
* interface. Keep it canonical-path keyed without reinstalling
|
||||
* the historical path-leaf qualifier. */
|
||||
u->str = u->usealias ? u->usealias : u->usepath;
|
||||
u->strlen = strlen(u->str);
|
||||
if (!u->useblank) {
|
||||
u->str = u->usealias ? u->usealias : u->usepath;
|
||||
u->strlen = strlen(u->str);
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
@@ -184,6 +348,8 @@ main(int argc, char **argv)
|
||||
const char *wwiout = NULL; /* -I <out.wwi>: M2 export-data producer */
|
||||
const char *testsupport = NULL;
|
||||
const char *testtarget = NULL;
|
||||
const char *packageinit = NULL;
|
||||
const char *initdispatch = NULL;
|
||||
int testmode = 0;
|
||||
int testpackage = 0;
|
||||
int commandpackage = 0;
|
||||
@@ -215,6 +381,18 @@ main(int argc, char **argv)
|
||||
commandpackage = 1;
|
||||
} else if (strcmp(a, "--entry") == 0) {
|
||||
entrymode = 1;
|
||||
} else if (strcmp(a, "--package-init-symbol") == 0) {
|
||||
if (i + 1 >= argc) {
|
||||
fputs("w6c: --package-init-symbol requires arg\n", stderr);
|
||||
return 2;
|
||||
}
|
||||
packageinit = argv[++i];
|
||||
} else if (strcmp(a, "--init-dispatch-symbol") == 0) {
|
||||
if (i + 1 >= argc) {
|
||||
fputs("w6c: --init-dispatch-symbol requires arg\n", stderr);
|
||||
return 2;
|
||||
}
|
||||
initdispatch = argv[++i];
|
||||
} else if (strcmp(a, "--test-support-module") == 0) {
|
||||
if (i + 1 >= argc) {
|
||||
fputs("w6c: --test-support-module requires arg\n", stderr);
|
||||
@@ -256,10 +434,14 @@ main(int argc, char **argv)
|
||||
}
|
||||
}
|
||||
if (src == NULL) {
|
||||
fputs("usage: w6c [-T|--test-package] [--command-package] [--entry] [--test-target-package path] [-c] [-I out.wwi] "
|
||||
fputs("usage: w6c [-T|--test-package] [--command-package] [--entry] [--package-init-symbol symbol] [--init-dispatch-symbol symbol] [--test-target-package path] [-c] [-I out.wwi] "
|
||||
"[--import path dep.wwi]... [--import-map source path]... [-o out.s] file.ww\n", stderr);
|
||||
return 2;
|
||||
}
|
||||
if (out != NULL && wwiout != NULL && strcmp(out, wwiout) == 0) {
|
||||
fputs("w6c: assembly and interface outputs must be distinct\n", stderr);
|
||||
return 2;
|
||||
}
|
||||
if (nimports > 0 && !sepmode) {
|
||||
fputs("w6c: --import requires -c\n", stderr);
|
||||
return 2;
|
||||
@@ -272,6 +454,19 @@ main(int argc, char **argv)
|
||||
fputs("w6c: --entry, --test-package, and --command-package require -c\n", stderr);
|
||||
return 2;
|
||||
}
|
||||
if ((packageinit != NULL || initdispatch != NULL) && !sepmode) {
|
||||
fputs("w6c: package initialization symbols require -c\n", stderr);
|
||||
return 2;
|
||||
}
|
||||
if (packageinit != NULL && packageinit[0] == '\0') {
|
||||
fputs("w6c: --package-init-symbol is empty\n", stderr);
|
||||
return 2;
|
||||
}
|
||||
if (initdispatch != NULL && (initdispatch[0] == '\0' || !entrymode)) {
|
||||
fputs("w6c: --init-dispatch-symbol requires --entry and a non-empty symbol\n",
|
||||
stderr);
|
||||
return 2;
|
||||
}
|
||||
if (testmode && testpackage) {
|
||||
fputs("w6c: -T and --test-package are mutually exclusive\n", stderr);
|
||||
return 2;
|
||||
@@ -427,27 +622,33 @@ main(int argc, char **argv)
|
||||
if (testsupport != NULL) c.test_module = testsupport;
|
||||
c.test_target = testtarget;
|
||||
c.sep_mode = sepmode;
|
||||
c.package_init_symbol = packageinit;
|
||||
check_file(&c, file);
|
||||
if (c.errs) return 1;
|
||||
|
||||
/* M2 export-data: write the `.wwi` after a clean check, before cgen.
|
||||
* Dead on the live path (no existing invocation passes -I); the
|
||||
* producer's check_exported_type may reject a dangling export. */
|
||||
/* Anonymous streams keep every named destination untouched until checking,
|
||||
* export writing, and lowering have all succeeded. A cgen fatal exits with
|
||||
* only kernel-owned anonymous files open. */
|
||||
FILE *wf = NULL;
|
||||
if (wwiout) {
|
||||
FILE *wf = fopen(wwiout, "wb");
|
||||
wf = tmpfile();
|
||||
if (wf == NULL) {
|
||||
fprintf(stderr, "w6c: cannot open %s\n", wwiout);
|
||||
fprintf(stderr, "w6c: cannot stage %s\n", wwiout);
|
||||
return 1;
|
||||
}
|
||||
if (wwi_emit(&c, wf, file) != 0 || fflush(wf) != 0
|
||||
|| ferror(wf)) {
|
||||
fclose(wf);
|
||||
return 1;
|
||||
}
|
||||
if (wwi_emit(&c, wf, file) != 0) { fclose(wf); return 1; }
|
||||
fclose(wf);
|
||||
}
|
||||
|
||||
FILE *of = stdout;
|
||||
if (out) {
|
||||
of = fopen(out, "wb");
|
||||
of = tmpfile();
|
||||
if (of == NULL) {
|
||||
fprintf(stderr, "w6c: cannot open %s\n", out);
|
||||
fprintf(stderr, "w6c: cannot stage %s\n", out);
|
||||
if (wf != NULL) fclose(wf);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -458,13 +659,46 @@ main(int argc, char **argv)
|
||||
* properties. Legacy raw invocations without -I remain entry-like; every
|
||||
* driver package now supplies -I, and only link roots add --entry. */
|
||||
cg.sep_isdep = (wwiout != NULL && !entrymode);
|
||||
cg.init_dispatch_symbol = initdispatch;
|
||||
cg_file(&cg, of, file);
|
||||
|
||||
if (of != stdout && (fflush(of) != 0 || ferror(of))) {
|
||||
fclose(of);
|
||||
if (wf != NULL) fclose(wf);
|
||||
return 1;
|
||||
}
|
||||
struct publication pub[2] = {0};
|
||||
int npub = 0;
|
||||
if (wwiout != NULL) {
|
||||
pub[npub].dst = wwiout;
|
||||
if (materialize_stream(wf, &pub[npub]) < 0) goto publish_fail;
|
||||
npub++;
|
||||
}
|
||||
if (out != NULL) {
|
||||
pub[npub].dst = out;
|
||||
if (materialize_stream(of, &pub[npub]) < 0) goto publish_fail;
|
||||
npub++;
|
||||
}
|
||||
if (wf != NULL) fclose(wf);
|
||||
if (of != stdout) fclose(of);
|
||||
if (publish_all(pub, npub) < 0) goto publish_free_fail;
|
||||
for (int i = 0; i < npub; i++) publication_free(&pub[i]);
|
||||
freearena(a);
|
||||
for (int i = 0; i < nimports; i++) free(imports[i].buf);
|
||||
free(imports);
|
||||
free(maps);
|
||||
free(buf);
|
||||
return 0;
|
||||
|
||||
publish_fail:
|
||||
if (wf != NULL) fclose(wf);
|
||||
if (of != stdout) fclose(of);
|
||||
for (int i = 0; i < 2; i++) {
|
||||
if (pub[i].stage != NULL) (void)unlink(pub[i].stage);
|
||||
publication_free(&pub[i]);
|
||||
}
|
||||
return 1;
|
||||
publish_free_fail:
|
||||
for (int i = 0; i < npub; i++) publication_free(&pub[i]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ static const char *
|
||||
wwi_use_path(Checker *c, const char *owner, int source, const char *alias)
|
||||
{
|
||||
for (Node *u = c->file->list; u; u = u->next) {
|
||||
if (u->kind != N_USE || u->str == NULL
|
||||
if (u->kind != N_USE || u->useblank || u->str == NULL
|
||||
|| u->sourceid != source || strcmp(u->str, alias) != 0)
|
||||
continue;
|
||||
int same = owner == NULL ? u->imported == 0
|
||||
@@ -592,6 +592,7 @@ factcmp(const void *a, const void *b)
|
||||
static int
|
||||
wwi_is_decl(Node *d)
|
||||
{
|
||||
if (d->initfn || d->initsynthetic) return 0;
|
||||
return d->kind == N_FNDECL || d->kind == N_TYPEDECL
|
||||
|| d->kind == N_DEF || d->kind == N_LET;
|
||||
}
|
||||
@@ -791,7 +792,7 @@ wwi_collect_decl(struct factset *fs, const char *owner, Node *d)
|
||||
static int
|
||||
wwi_use_owned(Node *u, const char *owner, int source)
|
||||
{
|
||||
return u->kind == N_USE && u->imported != 0
|
||||
return u->kind == N_USE && !u->useblank && u->imported != 0
|
||||
&& u->sourceid == source && wwi_mod_eq(u->module, owner);
|
||||
}
|
||||
|
||||
@@ -803,7 +804,7 @@ wwi_emit_imports(FILE *of, Node *file, const char *owner, int source,
|
||||
for (Node *u = file->list; u; u = u->next) {
|
||||
int owned = imported ? wwi_use_owned(u, owner, source)
|
||||
: u->kind == N_USE && u->imported == 0
|
||||
&& u->sourceid == source;
|
||||
&& !u->useblank && u->sourceid == source;
|
||||
if (owned) nuse++;
|
||||
}
|
||||
if (nuse == 0) return;
|
||||
@@ -813,7 +814,7 @@ wwi_emit_imports(FILE *of, Node *file, const char *owner, int source,
|
||||
for (Node *u = file->list; u; u = u->next) {
|
||||
int owned = imported ? wwi_use_owned(u, owner, source)
|
||||
: u->kind == N_USE && u->imported == 0
|
||||
&& u->sourceid == source;
|
||||
&& !u->useblank && u->sourceid == source;
|
||||
if (!owned) continue;
|
||||
us[k].path = u->usepath ? u->usepath : u->str;
|
||||
us[k].idx = k;
|
||||
@@ -836,7 +837,7 @@ wwi_primary_section_has(Checker *c, Node *file, struct factset *fs,
|
||||
struct declent *exports, int nexports, int source)
|
||||
{
|
||||
for (Node *u = file->list; u; u = u->next)
|
||||
if (u->kind == N_USE && u->imported == 0
|
||||
if (u->kind == N_USE && !u->useblank && u->imported == 0
|
||||
&& u->sourceid == source)
|
||||
return 1;
|
||||
for (int i = 0; i < fs->nprivate; i++)
|
||||
|
||||
Reference in New Issue
Block a user