diff options
Diffstat (limited to 'src/prog.c')
-rw-r--r-- | src/prog.c | 738 |
1 files changed, 557 insertions, 181 deletions
@@ -1,5 +1,5 @@ /* This file is part of Mailfromd. - Copyright (C) 2006-2019 Sergey Poznyakoff + Copyright (C) 2006-2024 Sergey Poznyakoff This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -60,7 +60,6 @@ size_t dataseg_reloc_count; struct rt_regex *regtab; size_t regcount; mu_opool_t regpool; - void code_init() @@ -149,7 +148,34 @@ code_peek(prog_counter_t pos) assert(pos < pmax); return prog[pos]; } + +static size_t transform_count; +static size_t transform_max; +static transform_t *transform_tab; +size_t +next_transform_index(void) +{ + if (transform_count == transform_max) { + transform_tab = mu_2nrealloc(transform_tab, + &transform_max, + sizeof(transform_tab[0])); + } + transform_tab[transform_count] = NULL; + return transform_count++; +} + +void +install_transform(size_t index, transform_t tp) +{ + transform_tab[index] = tp; +} + +transform_t +get_transform(size_t index) +{ + return transform_tab[index]; +} /* Regexps*/ @@ -213,7 +239,7 @@ disable_prog_trace(const char *modlist) } /* ======================================================================== - Drzewa w górê, rzeki w dó³. + Drzewa w górÄ™, rzeki w dół. Jacek Kaczmarski. "Upadek Ikara" @@ -225,8 +251,6 @@ disable_prog_trace(const char *modlist) See comment to env_fixup_autos, below. */ #define MAX_AUTO_PTR 128 -#define ENVF_MSGMOD 0x01 /* message modofication instruction has been used */ - struct exception_context { prog_counter_t pc; prog_counter_t tos; @@ -237,10 +261,11 @@ struct exception_context { struct environ_locus { unsigned long file; /* File name */ - unsigned int line; /* Line number */ + unsigned int line; /* Line number */ }; struct environ_cleanup_closure { + int when; void (*cleanup)(void *); void *data; }; @@ -262,6 +287,7 @@ struct eval_environ { struct environ_locus locus; /* Temporary heap space */ + int temp_active; size_t temp_start; size_t temp_size; @@ -271,14 +297,13 @@ struct eval_environ { /* Sendmail interaction data: */ SMFICTX *ctx; /* Milter Context */ + int conn_family; /* SMTP connection family. */ void *data; /* MTA symbol table */ /* methods to access the latter */ const char *(*getsym)(void *data, const char *str); int (*setreply)(void *data, char *code, char *xcode, char *message); void (*msgmod)(void *data, struct msgmod_closure *c); - int flags; - /* Regular expression matching */ regmatch_t *matches; /* Match map */ size_t matchcount; /* Number of used entries in matches */ @@ -292,7 +317,10 @@ struct eval_environ { its end before writing. */ mu_header_t header; /* Headers from stream, converted to a MU object. */ - + + /* Message modification queue */ + mu_list_t mmq; + /* Non-local exits */ struct exception_context *defcatch_ctx; struct exception_context *catch_ctx; @@ -301,8 +329,8 @@ struct eval_environ { void **bi_priv_array; /* Function clean-up sequence */ - mu_list_t cleanup_list; - + mu_list_t function_cleanup_list; + /* Exit information */ sfsistat status; /* Program exit status */ jmp_buf x_jmp; /* Return point for runtime errors */ @@ -421,63 +449,71 @@ _cleanup_compare(const void *a, const void *b) { struct environ_cleanup_closure const *closa = a; struct environ_cleanup_closure const *closb = b; - return closa->data != closb->data; + return !(closa->when == closb->when && closa->data == closb->data); } - + static void -_cleanup_destroy(void *item) +env_cleanup_list_create(mu_list_t *plist) { - struct environ_cleanup_closure *clos = item; - if (clos->cleanup) - clos->cleanup(clos->data); + mu_list_t list; + mu_list_create(&list);//FIXME: error checking + mu_list_set_comparator(list, _cleanup_compare); + *plist = list; } static void -env_create_cleanup_list(eval_environ_t env) +env_cleanup_list_append(mu_list_t list, int when, void *data, void (*func)(void *)) { - mu_list_create(&env->cleanup_list); - mu_list_set_destroy_item(env->cleanup_list, _cleanup_destroy); - mu_list_set_comparator(env->cleanup_list, _cleanup_compare); + struct environ_cleanup_closure *clos = mu_zalloc(sizeof(*clos)); + + clos->when = when; + clos->cleanup = func ? func : free; + clos->data = data; + mu_list_append(list, clos); +} + +static int +_cleanup_flush (void *item, void *data) +{ + struct environ_cleanup_closure *clos = item; + int when = *(int*)data; + + if (when == CLEANUP_ALWAYS || + clos->when == CLEANUP_ALWAYS || + clos->when == when) { + clos->cleanup(clos->data); + } + return 0; } void -env_function_cleanup_flush(eval_environ_t env, void *ptr) +env_function_cleanup_flush(eval_environ_t env, int when) { - if (ptr) { - struct environ_cleanup_closure clos; - clos.data = ptr; - mu_list_remove(env->cleanup_list, &clos); - } else { - mu_list_clear(env->cleanup_list); - } + mu_list_foreach(env->function_cleanup_list, _cleanup_flush, &when); + mu_list_clear(env->function_cleanup_list); } void -env_function_cleanup_del(eval_environ_t env, void *ptr) +env_function_cleanup_del(eval_environ_t env, int when, void *ptr) { int rc; struct environ_cleanup_closure clos, *cptr; + clos.when = when; clos.data = ptr; - rc = mu_list_locate(env->cleanup_list, &clos, (void**)&cptr); + rc = mu_list_locate(env->function_cleanup_list, &clos, (void**)&cptr); if (rc == 0) { cptr->cleanup = NULL; - mu_list_remove(env->cleanup_list, &clos); + mu_list_remove(env->function_cleanup_list, &clos); } } void -env_function_cleanup_add(eval_environ_t env, void *data, - void (*func)(void *)) +env_function_cleanup_add(eval_environ_t env, int when, void *data, void (*func)(void *)) { - struct environ_cleanup_closure *clos = mu_zalloc(sizeof(*clos)); - - clos->cleanup = func ? func : free; - clos->data = data; - mu_list_append(env->cleanup_list, clos); + env_cleanup_list_append(env->function_cleanup_list, when, data, func); } - /* A call to expand_dataseg (see below) invalidates any C variables that pointed to the dataseg before the call. To avoid dereferencing invalid @@ -494,7 +530,7 @@ static void env_register_auto(eval_environ_t env, void *ptr) { char *addr = *(char**)ptr; - + if (env->numautos == MAX_AUTO_PTR) runtime_error(env, "INTERNAL ERROR at %s:%d, please report", __FILE__, __LINE__); @@ -531,46 +567,39 @@ env_fixup_autos(eval_environ_t env, ptrdiff_t offset) int -expand_dataseg(eval_environ_t env, size_t count, const char *errtext) +expand_dataseg(eval_environ_t env, size_t count) { STKVAL *newds; ptrdiff_t offset; size_t new_stack_size; size_t diff; + enum { DATASEG_MAX = ((size_t)-1) / sizeof(STKVAL) }; switch (stack_expand_policy) { case stack_expand_add: diff = ((count + stack_expand_incr - 1) / stack_expand_incr) * stack_expand_incr; + if (DATASEG_MAX - diff < env->stack_size) + return -1; new_stack_size = env->stack_size + diff; break; case stack_expand_twice: - new_stack_size = 2 * env->stack_size; - if (new_stack_size < env->stack_size) { - if (errtext) - runtime_error(env, "%s", errtext); - else - return 1; - } - diff = new_stack_size - env->stack_size; - if (diff < count) { - if (errtext) - runtime_error(env, "%s", errtext); - else - return 1; - } + new_stack_size = env->stack_size; + do { + if (DATASEG_MAX / 2 < new_stack_size) + return -1; + new_stack_size *= 2; + diff = new_stack_size - env->stack_size; + } while (diff < count); } if ((stack_max_size && new_stack_size > stack_max_size) + || DATASEG_MAX - datasize < new_stack_size || (newds = realloc(env->dataseg, (new_stack_size + datasize) - * sizeof env->dataseg[0])) == NULL) { - if (errtext) - runtime_error(env, "%s", errtext); - else - return 1; - } + * sizeof env->dataseg[0])) == NULL) + return 1; offset = (char*)newds - (char*)env->dataseg; env->dataseg = newds; @@ -603,50 +632,72 @@ prog_trace(eval_environ_t env, const char *fmt, ...) buf); } +void instr_builtin(eval_environ_t env); void instr_funcall(eval_environ_t env); void instr_locus(eval_environ_t env); +void instr_catch(eval_environ_t env); + +static struct mu_locus_point * +stack_find_locus(eval_environ_t env, prog_counter_t pc, prog_counter_t off, struct mu_locus_point *pt) +{ + int i; + + for (i = 0; i < 32; i++, off++) { + if (pc > off && prog[pc - off].c_instr == instr_locus) { + pt->mu_file = (char*)(env->dataseg + + mf_cell_c_value(prog[pc - off + 1], ulong)); + pt->mu_line = mf_cell_c_value(prog[pc - off + 2], uint); + return pt; + } + } + return NULL; +} + +static void +stack_trace_entry(eval_environ_t env, prog_counter_t pc) +{ + char *name; + struct mu_locus_point *pt, ptbuf; + + if (mf_cell_instr(prog[pc-2]) == instr_funcall || + mf_cell_instr(prog[pc-2]) == instr_builtin) { + name = (char*)(env->dataseg + mf_cell_c_value(prog[pc-1], size)); + pc -= 2; + pt = stack_find_locus(env, pc, 3, &ptbuf); + } else if (mf_cell_instr(prog[pc-2]) == instr_catch) { + name = "(in catch)"; + pc -= 2; + pt = stack_find_locus(env, pc, 3, &ptbuf); + } else { + name = "(unknown)"; + pt = stack_find_locus(env, pc, 0, &ptbuf); + } + + if (pt) + mu_error("%04lu: %s:%u: %s", + (unsigned long) pc, + pt->mu_file, pt->mu_line, name); + else + mu_error("%04lu: %s", + (unsigned long) pc, name); +} void runtime_stack_trace(eval_environ_t env) { size_t base; - + mu_error(_("stack trace:")); + stack_trace_entry(env, env->pc); for (base = env->base; base < datasize + env->stack_size - 4; base = mf_c_val(env->dataseg[base + 1], size) + base + 1) { - int i; - prog_counter_t pc = mf_c_val(env->dataseg[base + 2], ulong) - 1; - char *name; - struct mu_locus_point *ploc = NULL, loc; - + prog_counter_t pc = mf_c_val(env->dataseg[base + 2], ulong); if (pc < 2) break; /*FIXME*/ - if (mf_cell_instr(prog[pc-2]) == instr_funcall) { - name = (char*)(env->dataseg + mf_cell_c_value(prog[pc-1], size)); - pc -= 2; - } else { - name = "(in catch)"; - pc -= 3; - } - - for (i = 0; i < 10; i++) { - if (pc > i + 3 - && prog[pc - i - 3].c_instr == instr_locus) { - loc.mu_file = (char*)(env->dataseg - + mf_cell_c_value(prog[pc - i - 2], size)); - loc.mu_line = mf_cell_c_value(prog[pc - i - 1], size); - ploc = &loc; - break; - } + stack_trace_entry(env, pc); + if (mf_cell_instr(prog[pc-2]) == instr_catch) { + stack_trace_entry(env, mf_c_val(env->dataseg[base + 5], size)); } - - if (ploc) - mu_error("%04lu: %s:%u: %s", - (unsigned long) pc, - ploc->mu_file, ploc->mu_line, name); - else - mu_error("%04lu: %s", - (unsigned long) pc, name); } mu_error(_("stack trace finishes")); } @@ -660,10 +711,11 @@ runtime_warning(eval_environ_t env, const char *fmt, ...) _("RUNTIME WARNING near %s:%u: "), ENV_LOC_FILE(env), ENV_LOC_LINE(env)); va_start(ap, fmt); - mu_diag_output(MU_DIAG_WARNING, fmt, ap); + mu_diag_cont_vprintf(fmt, ap); va_end(ap); + mu_diag_cont_printf("\n"); } - + void runtime_error(eval_environ_t env, const char *fmt, ...) { @@ -732,8 +784,9 @@ push(eval_environ_t env, STKVAL val) ("tos=%lu, toh=%lu", (unsigned long) env->tos, (unsigned long) env->toh)); - expand_dataseg(env, 1, - _("out of stack space; increase #pragma stacksize")); + if (expand_dataseg(env, 1)) + runtime_error(env, "%s", + _("out of stack space; increase #pragma stacksize")); } env->dataseg[env->tos--] = val; } @@ -750,9 +803,11 @@ size_t heap_reserve_words(eval_environ_t env, size_t words) { size_t off = env->toh; - if (env->toh + words > env->tos) - expand_dataseg(env, words, - _("heap overrun; increase #pragma stacksize")); + if (env->toh + words > env->tos) { + if (expand_dataseg(env, words)) + runtime_error(env, "%s", + _("heap overrun; increase #pragma stacksize")); + } env->toh += words; return off; } @@ -768,9 +823,11 @@ heap_tempspace(eval_environ_t env, size_t size) { size_t words = B2STACK(size); STKVAL ret; - if (env->toh + words > env->tos) - expand_dataseg(env, words, - _("heap overrun; increase #pragma stacksize")); + if (env->toh + words > env->tos) { + if (expand_dataseg(env, words)) + runtime_error(env, "%s", + _("heap overrun; increase #pragma stacksize")); + } mf_c_val(ret, ptr) = env->dataseg + env->toh; return ret; } @@ -780,14 +837,18 @@ heap_obstack_begin(eval_environ_t env) { env->temp_start = env->toh; env->temp_size = 0; + env->temp_active = 1; } void heap_obstack_cancel(eval_environ_t env) { - env->toh = env->temp_start; - env->temp_start = 0; - env->temp_size = 0; + if (env->temp_active) { + env->toh = env->temp_start; + env->temp_start = 0; + env->temp_size = 0; + env->temp_active = 0; + } } STKVAL @@ -797,9 +858,58 @@ heap_obstack_finish(eval_environ_t env) mf_c_val(ret, size) = env->temp_start; env->temp_start = 0; env->temp_size = 0; + env->temp_active = 0; return ret; } +static inline size_t +heap_obstack_size(eval_environ_t env) +{ + return (env->tos - env->toh - B2STACK(env->temp_size)) + * sizeof env->dataseg[0]; +} + +void +heap_obstack_vsprintf(eval_environ_t env, const char *fmt, va_list ap) +{ + size_t n; + while (1) { + size_t size; + va_list apc; + + if (env->tos == env->toh) + if (expand_dataseg(env, B2STACK(strlen(fmt)))) { + runtime_error(env, "%s", + _("memory chunk too big to fit into heap")); + } + + size = heap_obstack_size(env); + va_copy(apc, ap); + n = vsnprintf((char*) env_data_ref(env, env->temp_start) + env->temp_size, + size, fmt, apc); + va_end(apc); + if (n >= size) { + if (expand_dataseg(env, B2STACK(n))) { + runtime_error(env, "%s", + _("memory chunk too big to fit into heap")); + } + continue; + } + break; + } + env->temp_size += n; + env->toh += B2STACK(n); +} + +void +heap_obstack_sprintf(eval_environ_t env, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + heap_obstack_vsprintf(env, fmt, ap); + va_end(ap); +} + void * heap_obstack_grow(eval_environ_t env, void * MFL_DATASEG ptr, size_t size) { @@ -807,9 +917,11 @@ heap_obstack_grow(eval_environ_t env, void * MFL_DATASEG ptr, size_t size) char *ret; env_register_auto(env, (void*) &ptr); - if (env->tos - env->toh < words + B2STACK(env->temp_size)) - expand_dataseg(env, words, - _("memory chunk too big to fit into heap")); + if (env->tos - env->toh < words + B2STACK(env->temp_size)) { + if (expand_dataseg(env, words)) + runtime_error(env, "%s", + _("memory chunk too big to fit into heap")); + } ret = (char*) env_data_ref(env, env->temp_start) + env->temp_size; if (ptr) memmove(ret, ptr, size); @@ -819,6 +931,26 @@ heap_obstack_grow(eval_environ_t env, void * MFL_DATASEG ptr, size_t size) return ret; } +void +heap_obstack_reclaim(eval_environ_t env, size_t size) +{ + if (size > env->temp_size) + abort(); + env->temp_size -= size; + env->toh -= B2STACK(size); +} + +void +heap_obstack_truncate(eval_environ_t env, size_t size) +{ + if (size == env->temp_size) + return; + else if (size > env->temp_size) + heap_obstack_grow(env, NULL, size - env->temp_size); + else + heap_obstack_reclaim(env, env->temp_size - size); +} + void * heap_obstack_base(eval_environ_t env) { @@ -854,7 +986,7 @@ pushn(eval_environ_t env, long n) void instr_locus(eval_environ_t env) { - env->locus.file = mf_c_val(get_immediate(env, 0), uint); + env->locus.file = mf_c_val(get_immediate(env, 0), ulong); env->locus.line = mf_c_val(get_immediate(env, 1), uint); if (PROG_TRACE_ENGINE) prog_trace(env, "LOCUS"); @@ -865,8 +997,8 @@ void dump_locus(prog_counter_t i) { printf("\"%s\" %lu", - (char*) (dataseg + mf_cell_c_value(prog[i], size)), - (unsigned long) mf_cell_c_value(prog[i+1], size)); + (char*) (dataseg + mf_cell_c_value(prog[i], ulong)), + (unsigned long) mf_cell_c_value(prog[i+1], uint)); } /* Stack manipulation instructions */ @@ -884,9 +1016,42 @@ instr_xchg(eval_environ_t env) void instr_dup(eval_environ_t env) { - if (PROG_TRACE_ENGINE) + if (PROG_TRACE_ENGINE) prog_trace(env, "DUP"); - push(env, get_arg(env, 0)); + push(env, (STKVAL)0L); + env->dataseg[env->tos+1] = env->dataseg[env->tos+2]; +} + +/* + * push COUNT + * push OFFSET + * dupn FRAME + * + * Pushes on stack COUNT stack words starting from OFFSET in the given + * FRAME, in the same order they appear on stack. + */ +void +instr_dupn(eval_environ_t env) +{ + size_t frame = mf_c_val(get_immediate(env, 0), size); + long count = mf_c_val(get_arg(env, 1), long); + long off = mf_c_val(get_arg(env, 0), long); + size_t i; + + if (count < 0) + runtime_error(env, _("dupn: negative count")); + if (PROG_TRACE_ENGINE) + prog_trace(env, "DUPN frame=%zu off=%ld count=%zu", + frame, off, count); + + adjust_stack(env, 2); + + for (i = count; i > 0; i--) { + push(env, (STKVAL)0L); + env->dataseg[env->tos+1] = env->dataseg[env_base(env, frame) + off + i - 1]; + } + + advance_pc(env, 1); } void @@ -925,7 +1090,7 @@ instr_memstk(eval_environ_t env) push(env, env_base(env, frame) + off) However, push() itself might cause dataseg expansion, - which would change tos/toh pointers and render the previously + which would change tos/base pointers and render the previously computed argument value invalid. The correct way is to push a placeholder value first, thereby occupying stack slot and eventually causing dataseg expansion, and then compute val @@ -997,10 +1162,12 @@ instr_stkalloc(eval_environ_t env) (unsigned long) env->tos, (unsigned long) env->toh, n)); - expand_dataseg(env, env->toh - (env->tos - n), - _("Out of stack space; increase #pragma stacksize")); + if (expand_dataseg(env, env->toh - (env->tos - n))) + runtime_error(env, "%s", + _("Out of stack space; increase #pragma stacksize")); } env->tos -= n; + memset(env->dataseg + env->tos, 0, n * sizeof(env->dataseg[0])); advance_pc(env, 1); } @@ -1135,7 +1302,7 @@ dump_symbol(prog_counter_t i) (char *) (dataseg + mf_cell_c_value(prog[i], size))); } -/* Comparation instructions */ +/* Comparison instructions */ void instr_eqn(eval_environ_t env) { @@ -1325,7 +1492,7 @@ instr_jmp(eval_environ_t env) advance_pc(env, off+1); } -/* Longean instructions */ +/* Boolean instructions */ void instr_not(eval_environ_t env) { @@ -1336,7 +1503,7 @@ instr_not(eval_environ_t env) pushn(env, !v); } -/* Bitwise arithmetics */ +/* Bitwise arithmetic */ void instr_logand(eval_environ_t env) { @@ -1588,7 +1755,61 @@ dump_regcomp(prog_counter_t i) printf("%s", regex_flags_to_string(rtx->regflags, buffer, sizeof buffer)); } + +void +instr_sedcomp(eval_environ_t env) +{ + char * MFL_DATASEG str; + size_t index = mf_c_val(get_immediate(env, 0), size); + transform_t *tp = &transform_tab[index]; + int flags = mf_c_val(get_immediate(env, 1), int); + + get_string_arg(env, 0, &str); + if (PROG_TRACE_ENGINE) + prog_trace(env, "SUBCOMP %s %zu %d", str, index, flags); + + advance_pc(env, 2); + adjust_stack(env, 1); + + transform_free(*tp); + + *tp = transform_compile(str, flags); + if (*tp == NULL) + runtime_error(env, + _("invalid transform string \"%s\": %s"), + str, + transform_error_string()); + + push(env, (STKVAL) index); +} + +void +dump_sedcomp(prog_counter_t i) +{ + size_t index = mf_cell_c_value(prog[i], size); + int flags = mf_cell_c_value(prog[i+1], int); + printf("%zu %d", index, flags); +} + +void +instr_sed(eval_environ_t env) +{ + char * MFL_DATASEG arg; + long i = mf_c_val(get_arg(env, 0), long); + char *res; + + get_string_arg(env, 1, &arg); + + if (PROG_TRACE_ENGINE) + prog_trace(env, "SED %s %ld", arg, i); + + adjust_stack(env, 2); + res = transform_string(transform_tab[i], arg); + pushs(env, res); + free(res); +} + void instr_fnmatch(eval_environ_t env) { @@ -1711,7 +1932,7 @@ instr_result(eval_environ_t env) SP(code), SP(xcode), SP(message)); - + trace("%s%s:%u: %s %s %s %s", mailfromd_msgid(env->ctx), ENV_LOC_FILE(env), ENV_LOC_LINE(env), @@ -1725,11 +1946,12 @@ instr_result(eval_environ_t env) if (xcode[0] == 0) xcode = NULL; - if (status == SMFIS_ACCEPT && (env->flags & ENVF_MSGMOD)) + if (status == SMFIS_ACCEPT && env_msgmod_count(env)) runtime_warning(env, _("`accept' causes previous message " "modification commands to be ignored; " "call mmq_purge() prior to `accept', " "to suppress this warning")); + env->status = status; env->setreply(env->data, code, xcode, message); advance_pc(env, 1); @@ -1763,7 +1985,7 @@ instr_header(eval_environ_t env) msgmod_opcode_str(opcode), name, SP(value)); - env_msgmod(env, opcode, name, value, 1); + env_msgmod_append(env, opcode, name, value, 1); } void @@ -1813,21 +2035,15 @@ instr_concat(eval_environ_t env) } void -dump_adjust(prog_counter_t i) -{ - printf("%lu ", mf_cell_c_value(prog[i], ulong)); -} - -void instr_asgn(eval_environ_t env) { STKVAL val = get_arg(env, 1); size_t dest = mf_c_val(get_arg(env, 0), size); adjust_stack(env, 2); if (PROG_TRACE_ENGINE) - prog_trace(env, "ASGN %lu=%u", + prog_trace(env, "ASGN %lu=%lu", (unsigned long) dest, - mf_c_val(val, uint)); + mf_c_val(val, ulong)); env->dataseg[dest] = val; } @@ -1835,7 +2051,7 @@ void instr_catch(eval_environ_t env) { long off = mf_c_val(get_immediate(env, 0), long); - unsigned toff = mf_c_val(get_immediate(env, 1), uint); + size_t toff = mf_c_val(get_immediate(env, 1), size); size_t count = mf_c_val(env->dataseg[toff], size); STKVAL *tab = (STKVAL *) (env->dataseg + toff + 1); size_t i; @@ -1880,7 +2096,6 @@ instr_throw(eval_environ_t env) { unsigned long n = mf_c_val(get_immediate(env, 0), ulong); size_t off = mf_c_val(get_arg(env, 0), size); - advance_pc(env, 1); adjust_stack(env, 1); if (n > exception_count) runtime_error(env, _("invalid exception number: %lu"), n); @@ -1892,8 +2107,8 @@ instr_throw(eval_environ_t env) void dump_throw(prog_counter_t i) { - printf("%s (%u)", mf_exception_str(mf_cell_c_value(prog[i], uint)), - mf_cell_c_value(prog[i], uint)); + printf("%s (%lu)", mf_exception_str(mf_cell_c_value(prog[i], ulong)), + mf_cell_c_value(prog[i], ulong)); } @@ -1915,7 +2130,6 @@ instr_return(eval_environ_t env) if (PROG_TRACE_ENGINE) prog_trace(env, "RETURN"); env_leave_frame(env, 0); - env->pc--; } void @@ -1924,25 +2138,40 @@ instr_retcatch(eval_environ_t env) prog_counter_t pc = env->pc; if (PROG_TRACE_ENGINE) prog_trace(env, "RETCATCH"); - env_leave_frame(env, 2); + env_leave_frame(env, 3); env->pc = pc; } +/* + * Return true if the given bit is set in the exception mask starting at + * offset off. + */ +static inline int +env_exmask_isset(eval_environ_t env, size_t off, int bit) +{ + return ((bitmask_bits_t)mf_c_val(env->dataseg[off + 1 + BIT_WORD(bit)], uint)) & BIT_MASK(bit); +} + void instr_saveex(eval_environ_t env) { - unsigned off = mf_c_val(get_immediate(env, 0), uint); + size_t off = mf_c_val(get_immediate(env, 0), size); size_t count = mf_c_val(env->dataseg[off], size); - STKVAL *tab = (STKVAL *) (env->dataseg + off + 1); size_t i; if (PROG_TRACE_ENGINE) - prog_trace(env, "SAVEEX %x (%lu ex.)", off, + prog_trace(env, "SAVEEX %lx (%lu ex.)", + (unsigned long) off, (unsigned long) count); advance_pc(env, 1); + /* + * Note: in the loop that follows each push can lead to data segment + * expansion, therefore the actual start address of the exception table + * needs to be computed in each iteration using env_exmask_isset. + */ for (i = 0; i < count * NBMBITS; i++) - if (((bitmask_bits_t)mf_c_val(tab[BIT_WORD(i)], uint)) & BIT_MASK(i)) { + if (env_exmask_isset(env, off, i)) { mu_debug(MF_SOURCE_PROG, MU_DEBUG_TRACE9, ("Push Exception: %lu %lu <- pc=%lu, tos=%lu, base=%lu", (unsigned long) i, @@ -1974,14 +2203,14 @@ dump_saveex(prog_counter_t ctr) void instr_restex(eval_environ_t env) { - unsigned off = mf_c_val(pop(env), uint); + size_t off = mf_c_val(pop(env), size); size_t count = mf_c_val(env->dataseg[off], size); STKVAL *tab = (STKVAL *) (env->dataseg + off + 1); size_t i; if (PROG_TRACE_ENGINE) - prog_trace(env, "RESTEX %x (%lu ex.)", - off, (unsigned long) count); + prog_trace(env, "RESTEX %lx (%lu ex.)", + (unsigned long) off, (unsigned long) count); if (!count) return; i = count * NBMBITS - 1; @@ -2003,6 +2232,12 @@ instr_restex(eval_environ_t env) } void +dump_adjust(prog_counter_t i) +{ + printf("%lu ", mf_cell_c_value(prog[i], ulong)); +} + +void instr_adjust(eval_environ_t env) { long nargs = mf_c_val(get_immediate(env, 0), long); @@ -2012,6 +2247,30 @@ instr_adjust(eval_environ_t env) advance_pc(env, 1); } +/* + * Adjust stack after return from a variadic function. + * + * When such function is called, it is passed the actual number + * of arguments in its first (hidden) argument. + * + * The inline parameter to adjustx gives the number of arguments the + * function is declared to take (both mandatory and optional). These + * are always on stack, so this value is used instead of the actual + * number if the latter is less than the former. This can happen, + * e.g. if a function takes both optional and variable arguments and + * is given only mandatory ones. + */ +void +instr_adjustx(eval_environ_t env) +{ + long nargs = mf_c_val(get_immediate(env, 0), long); + size_t n = mf_c_val(pop(env), size); + if (PROG_TRACE_ENGINE) + prog_trace(env, "ADJUSTX %zu %ld", n, nargs); + adjust_stack(env, n < nargs ? nargs : n); + advance_pc(env, 1); +} + void instr_popreg(eval_environ_t env) { @@ -2082,14 +2341,15 @@ instr_xlat(eval_environ_t env) void instr_xlats(eval_environ_t env) { - unsigned long i; - unsigned long count = mf_c_val(get_immediate(env, 0), uint); + size_t i; + size_t count = mf_c_val(get_immediate(env, 0), size); size_t off = mf_c_val(get_immediate(env, 1), size); STKVAL *tab = (STKVAL *) (env->dataseg + off); char *str = (char*) env_data_ref(env, mf_c_val(env->reg, size)); if (PROG_TRACE_ENGINE) - prog_trace(env, "XLATS %lu %lu", count, (unsigned long) off); + prog_trace(env, "XLATS %lu %lu", + (unsigned long) count, (unsigned long) off); advance_pc(env, 2); for (i = 0; i < count; i += 2) { if (strcmp((char*)(env->dataseg + mf_c_val(tab[i], size)), str) == 0) { @@ -2104,12 +2364,12 @@ instr_xlats(eval_environ_t env) void dump_xlat(prog_counter_t i) { - unsigned long j; - unsigned long count = mf_cell_c_value(prog[i], ulong); + size_t j; + size_t count = mf_cell_c_value(prog[i], size); size_t off = mf_cell_c_value(prog[i+1], size); STKVAL *tab = (STKVAL *) (dataseg + off); - printf("%lu %lu ", count, (unsigned long) off); + printf("%lu %lu ", (unsigned long) count, (unsigned long) off); for (j = 0; j < count; j += 2) printf("(%ld %ld) ", mf_c_val(tab[j], long), mf_c_val(tab[j+1], long)); } @@ -2211,6 +2471,7 @@ env_init(eval_environ_t env) env->status = SMFIS_CONTINUE; env->tos = datasize + env->stack_size - 1; env->base = 0; + env->pc = 0; mf_c_val(env->reg, long) = 0; //FIXME env->matches = NULL; @@ -2242,17 +2503,9 @@ env_init_dataseg(eval_environ_t env) } void -env_make_frame0(eval_environ_t env) -{ - push(env, (STKVAL) 0); - push(env, (STKVAL) (env->base - env->tos)); - env->base = env->tos; -} - -void env_make_frame(eval_environ_t env) { - push(env, (STKVAL) (env->pc + 1)); + push(env, (STKVAL) env->pc); push(env, (STKVAL) (env->base - env->tos)); env->base = env->tos; } @@ -2320,9 +2573,10 @@ env_vsprintf(eval_environ_t env, const char *biname, while (1) { size_t k; size_t size; + va_list apc; if (env->tos == env->toh) - if (expand_dataseg(env, B2STACK(strlen(fmt)), NULL)) { + if (expand_dataseg(env, B2STACK(strlen(fmt)))) { env_vsprintf_error(fmt, ap); break; } @@ -2334,17 +2588,19 @@ env_vsprintf(eval_environ_t env, const char *biname, if (biname) { n = snprintf(s, size, "%s: ", biname); if (n >= size) { - n += strlen(fmt); /* rough estimation */ - if (expand_dataseg(env, B2STACK(n), NULL)) { + n += strlen(fmt); /* rough estimate */ + if (expand_dataseg(env, B2STACK(n))) { env_vsprintf_error(fmt, ap); break; } continue; } } - k = vsnprintf(s + n, size - n, fmt, ap); + va_copy(apc, ap); + k = vsnprintf(s + n, size - n, fmt, apc); + va_end(apc); if (k >= size) { - if (expand_dataseg(env, B2STACK(k), NULL)) { + if (expand_dataseg(env, B2STACK(k))) { env_vsprintf_error(fmt, ap); break; } @@ -2364,13 +2620,15 @@ env_throw_0(eval_environ_t env, mf_exception status, size_t off) { prog_counter_t pc; - env_function_cleanup_flush(env, NULL); + env_function_cleanup_flush(env, CLEANUP_THROW); if (status > exception_count) runtime_error(env, _("unknown exception: %d: %s"), status, (char*) env_data_ref(env, off)); pc = env->catch_ctx[status].pc; if (pc) { + prog_counter_t throw_pc = env->pc; + /* Restore tos */ env->tos = TOS_INVARIANT(env, env->catch_ctx[status].tos); env->base = TOS_INVARIANT(env, env->catch_ctx[status].base); @@ -2379,6 +2637,7 @@ env_throw_0(eval_environ_t env, mf_exception status, size_t off) /* Fixup the program counter */ env->pc = pc - 1; /* Generate normal entry frame */ + push(env, (STKVAL) throw_pc); push(env, (STKVAL) off); env_push_number(env, status); env_make_frame(env); @@ -2406,6 +2665,9 @@ env_throw_bi(eval_environ_t env, mf_exception status, const char *biname, { va_list ap; size_t off; + + heap_obstack_cancel(env); + va_start(ap, fmt); off = env_vsprintf(env, biname, fmt, ap); va_end(ap); @@ -2453,7 +2715,7 @@ env_capture_start(eval_environ_t env) /* Drop any previously captured message data */ env_free_captured(env); mu_header_destroy(&env->header); - /* Truncate existing stream and reposition to its + /* Truncate existing stream and re-position to its beginning */ rc = mu_stream_truncate(env->stream, 0); if (rc == 0 && @@ -2595,7 +2857,7 @@ env_get_header(eval_environ_t env, mu_header_t *hdr) } text[total] = 0; - /* FIXME: Reposition the stream at its end. + /* FIXME: Re-position the stream at its end. This call may happen in the middle of capturing so I have to make sure to not disturb the capturing process. @@ -2622,19 +2884,30 @@ env_get_header(eval_environ_t env, mu_header_t *hdr) *hdr = env->header; return 0; } + +/* MMQ */ void -env_clear_msgmod(eval_environ_t env) +env_msgmod_clear(eval_environ_t env) { if (PROG_TRACE_ENGINE) prog_trace(env, "Clearing message modification queue"); - env->msgmod(env->data, NULL); - env->flags &= ~ENVF_MSGMOD; + if (env->msgmod) + env->msgmod(env->data, NULL); + mu_list_clear(env->mmq); } void -env_msgmod(eval_environ_t env, enum msgmod_opcode opcode, - const char *name, const char *value, unsigned idx) +destroy_msgmod_closure(void *item) +{ + struct msgmod_closure *cmd = item; + free(cmd->name); + free(cmd->value); +} + +void +env_msgmod_append(eval_environ_t env, enum msgmod_opcode opcode, + const char *name, const char *value, unsigned idx) { struct msgmod_closure *cp = mu_alloc(sizeof *cp); @@ -2647,8 +2920,35 @@ env_msgmod(eval_environ_t env, enum msgmod_opcode opcode, cp->name = name ? mu_strdup(name) : NULL; cp->value = value ? mu_strdup(value) : NULL; cp->idx = idx; - env->msgmod(env->data, cp); - env->flags |= ENVF_MSGMOD; + + if (!env->mmq) { + mu_list_create(&env->mmq); + mu_list_set_destroy_item(env->mmq, destroy_msgmod_closure); + } + mu_debug(MF_SOURCE_ENGINE, MU_DEBUG_TRACE5, + ("adding msgmod_closure: %s \"%s\" %s %u", + msgmod_opcode_str(cp->opcode), + SP(cp->name), SP(cp->value), cp->idx)); + if (env->msgmod) + env->msgmod(env->data, cp); + mu_list_append(env->mmq, cp); |