feat: replace M4 callback writers with safe handles

This commit is contained in:
2026-08-16 18:27:26 +09:00
parent 42abc0d7e7
commit 351e5dbb23
11 changed files with 54 additions and 72 deletions
+6 -15
View File
@@ -375,9 +375,8 @@ static void check_format_call(FeCheckerState *s, FeNode *n)
if (offset) {
if (!fmt_node) { err(s->c,n->loc,"@fprint requires a writer"); return; }
t=check_expr(s,fmt_node);
if (!(t && t->kind==FE_TYPE_REF && t->ref_mut &&
format_is_writer_type(t->elem)))
err(s->c,fmt_node->loc,"@fprint requires &mut io.Writer");
if (!format_is_writer_type(t))
err(s->c,fmt_node->loc,"@fprint requires io.Writer");
fmt_node=fmt_node->next;
}
if (strcmp(n->text,"@sprint")==0) {
@@ -679,18 +678,9 @@ static FeType *check_expr(FeCheckerState *s, FeNode *n)
if (n->a && n->a->kind==FE_N_MEMBER && n->a->a &&
n->a->a->kind==FE_N_IDENT && n->a->a->text &&
strcmp(n->a->a->text,"io")==0 && n->a->b && n->a->b->text &&
(strcmp(n->a->b->text,"buf_writer")==0 ||
strcmp(n->a->b->text,"null_writer")==0)) {
strcmp(n->a->b->text,"null_writer")==0) {
FeNode *arg=n->children;
if (strcmp(n->a->b->text,"buf_writer")==0) {
if (!arg) err(c,n->loc,"io.buf_writer requires a buffer");
else {
a=check_expr(s,arg);
if (!(a && a->kind==FE_TYPE_REF && a->ref_mut &&
format_is_slice_u8(a->elem)))
err(c,arg->loc,"io.buf_writer requires &mut []u8 buffer");
}
} else if (arg) err(c,n->loc,"io.null_writer takes no arguments");
if (arg) err(c,n->loc,"io.null_writer takes no arguments");
n->sem_type=fe_type_intern(&c->types,"io.Writer");
return n->sem_type;
}
@@ -760,7 +750,8 @@ static FeType *check_expr(FeCheckerState *s, FeNode *n)
if (n->kind == FE_N_MEMBER) {
if (n->a && n->a->kind==FE_N_IDENT && n->a->text &&
strcmp(n->a->text,"io")==0 && n->b && n->b->text &&
strcmp(n->b->text,"stdout")==0) {
(strcmp(n->b->text,"stdout")==0 ||
strcmp(n->b->text,"stderr")==0)) {
n->sem_type=fe_type_intern(&c->types,"io.Writer");
return n->sem_type;
}
+12 -21
View File
@@ -251,22 +251,20 @@ static void emit_type_helpers(FeEmitter *e)
static void emit_m4_runtime(FeEmitter *e)
{
fputs("typedef struct { unsigned char *p; unsigned long n; } fe_m4_slice;\n",e->out);
fputs("typedef struct { void *ctx; unsigned short (*write_fn)(void *, const unsigned char *, unsigned long); } fe_writer;\n",e->out);
fputs("typedef struct { unsigned char tag; unsigned short handle; } fe_writer;\n",e->out);
fputs("unsigned short fe_m4_error;\n",e->out);
fputs("unsigned short fe_m4_stdout_write(void *ctx, const unsigned char *p, unsigned long n) { (void)ctx; return fwrite(p,1,(size_t)n,stdout)==(size_t)n ? 0 : 1; }\n",e->out);
fputs("unsigned short fe_m4_null_write(void *ctx, const unsigned char *p, unsigned long n) { (void)ctx; (void)p; (void)n; return 0; }\n",e->out);
fputs("unsigned short fe_m4_buf_write(void *ctx, const unsigned char *p, unsigned long n) { fe_m4_slice *b=(fe_m4_slice*)ctx; unsigned long k=n<b->n?n:b->n; if(k) memcpy(b->p,p,(size_t)k); b->p+=k; b->n-=k; return 0; }\n",e->out);
fputs("fe_writer fe_m4_stdout_writer(void) { fe_writer w; w.ctx=0; w.write_fn=fe_m4_stdout_write; return w; }\n",e->out);
fputs("fe_writer fe_m4_null_writer(void) { fe_writer w; w.ctx=0; w.write_fn=fe_m4_null_write; return w; }\n",e->out);
fputs("fe_writer fe_m4_buf_writer(fe_m4_slice *b) { fe_writer w; w.ctx=b; w.write_fn=fe_m4_buf_write; return w; }\n",e->out);
fputs("fe_writer fe_m4_writer(unsigned char tag, unsigned short handle) { fe_writer w; w.tag=tag; w.handle=handle; return w; }\n",e->out);
fputs("fe_writer fe_m4_stdout_writer(void) { return fe_m4_writer(0,1); }\n",e->out);
fputs("fe_writer fe_m4_stderr_writer(void) { return fe_m4_writer(1,2); }\n",e->out);
fputs("fe_writer fe_m4_null_writer(void) { return fe_m4_writer(3,0); }\n",e->out);
fputs("/* bounded sprint stack; overflow traps instead of corrupting an outer call */\n#define FE_M4_SPRINT_DEPTH 8\n",e->out);
fputs("typedef struct { fe_m4_slice b; unsigned long start_n; } fe_m4_sprint_frame;\n",e->out);
fputs("static fe_m4_sprint_frame fe_m4_sprint_stack[FE_M4_SPRINT_DEPTH];\n",e->out);
fputs("static unsigned fe_m4_sprint_depth;\n",e->out);
fputs("void fe_m4_sprint_begin(fe_m4_slice *b) { if (fe_m4_sprint_depth>=FE_M4_SPRINT_DEPTH) abort(); fe_m4_sprint_stack[fe_m4_sprint_depth].b=*b; fe_m4_sprint_stack[fe_m4_sprint_depth].start_n=b->n; ++fe_m4_sprint_depth; }\n",e->out);
fputs("fe_writer fe_m4_sprint_writer(void) { return fe_m4_buf_writer(&fe_m4_sprint_stack[fe_m4_sprint_depth-1].b); }\n",e->out);
fputs("fe_writer fe_m4_sprint_writer(void) { return fe_m4_writer(4,(unsigned short)(fe_m4_sprint_depth-1)); }\n",e->out);
fputs("unsigned long fe_m4_sprint_finish(void) { unsigned long result; if (!fe_m4_sprint_depth) abort(); --fe_m4_sprint_depth; result=fe_m4_sprint_stack[fe_m4_sprint_depth].start_n-fe_m4_sprint_stack[fe_m4_sprint_depth].b.n; return result; }\n",e->out);
fputs("unsigned short fe_m4_write_bytes(fe_writer w, const unsigned char *p, unsigned long n) { return w.write_fn ? w.write_fn(w.ctx,p,n) : 1; }\n",e->out);
fputs("unsigned short fe_m4_write_bytes(fe_writer w, const unsigned char *p, unsigned long n) { if(w.tag==0) return fwrite(p,1,(size_t)n,stdout)==(size_t)n?0:1; if(w.tag==1) return fwrite(p,1,(size_t)n,stderr)==(size_t)n?0:1; if(w.tag==3) return 0; if(w.tag==4 && w.handle<FE_M4_SPRINT_DEPTH) { fe_m4_slice *b=&fe_m4_sprint_stack[w.handle].b; unsigned long k=n<b->n?n:b->n; if(k) memcpy(b->p,p,(size_t)k); b->p+=k; b->n-=k; return 0; } return 1; }\n",e->out);
fputs("unsigned short fe_m4_write_cstr(fe_writer w, const char *p) { return fe_m4_write_bytes(w,(const unsigned char*)p,(unsigned long)strlen(p)); }\n",e->out);
fputs("#define fe_m4_write_slice(w,s) fe_m4_write_bytes((w),(s).p,(s).n)\n",e->out);
fputs("unsigned short fe_m4_write_int(fe_writer w, long v) { char b[40]; sprintf(b,\"%ld\",v); return fe_m4_write_cstr(w,b); }\n",e->out);
@@ -456,13 +454,8 @@ static void emit_m4_piece(FILE *out, const char *fmt, unsigned long begin,
static void emit_m4_writer(FeEmitter *e, FeNode *arg, int buffer)
{
if (buffer) {
fputs("fe_m4_buf_writer((fe_m4_slice*)&",e->out);
if (arg && arg->kind==FE_N_UNARY && arg->text &&
(strcmp(arg->text,"&")==0 || strcmp(arg->text,"&mut")==0)) emit_expr(e,arg->a);
else emit_expr(e,arg);
fputs(")",e->out);
} else if (arg && arg->kind==FE_N_UNARY && arg->text &&
(void)buffer;
if (arg && arg->kind==FE_N_UNARY && arg->text &&
(strcmp(arg->text,"&")==0 || strcmp(arg->text,"&mut")==0)) {
emit_expr(e,arg->a);
} else if (arg && arg->kind==FE_N_CALL && arg->a &&
@@ -817,10 +810,6 @@ static void emit_expr(FeEmitter *e, FeNode *n)
fputs("()",e->out);
special=1;
}
else if(n->a && n->a->kind==FE_N_MEMBER && n->a->a &&
n->a->a->kind==FE_N_IDENT && n->a->a->text &&
strcmp(n->a->a->text,"io")==0 && n->a->b && n->a->b->text &&
strcmp(n->a->b->text,"buf_writer")==0 && n->children) { emit_m4_writer(e,n->children,1); special=1; }
else if(n->a && n->a->kind==FE_N_MEMBER && n->a->a &&
n->a->a->kind==FE_N_IDENT && n->a->a->text &&
strcmp(n->a->a->text,"io")==0 && n->a->b && n->a->b->text &&
@@ -861,7 +850,9 @@ static void emit_expr(FeEmitter *e, FeNode *n)
FeVariantType *v;
if(n->a && n->a->kind==FE_N_IDENT && n->a->text &&
strcmp(n->a->text,"io")==0 && n->b && n->b->text &&
strcmp(n->b->text,"stdout")==0) fputs("fe_m4_stdout_writer()",e->out);
(strcmp(n->b->text,"stdout")==0 || strcmp(n->b->text,"stderr")==0))
fputs(strcmp(n->b->text,"stderr")==0 ?
"fe_m4_stderr_writer()" : "fe_m4_stdout_writer()",e->out);
else if(n->a && n->a->sem_type && n->a->sem_type->kind==FE_TYPE_REF &&
n->b && n->b->text && strcmp(n->b->text,"^")==0) {
fputs("(*",e->out); emit_expr(e,n->a); fputs(")",e->out);