/* This file is part of Mailfromd. Copyright (C) 2005, 2006, 2007, 2008 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 the Free Software Foundation; either version 3, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #define MARK_LOCUS() \ do if (!*old_locus || !LOCUS_EQ(*old_locus, &node->locus)) { \ struct literal *lit = literal_lookup(node->locus.file); \ *old_locus = &node->locus; \ code_op(opcode_locus); \ code_immediate((void*)lit->off); \ code_immediate((void*)node->locus.line); \ } while (0) void code_memref(NODE *node) { switch (node->v.var_ref.variable->storage_class) { case storage_extern: code_op(opcode_push); code_immediate((void*)node->v.var_ref.variable->off); break; case storage_auto: code_op(opcode_memstk); code_immediate((void*)node->v.var_ref.nframes); code_immediate((void*)-node->v.var_ref.variable->off); break; case storage_param: code_op(opcode_memstk); code_immediate((void*)node->v.var_ref.nframes); code_immediate((void*)(node->v.var_ref.variable->off + 2)); break; } } /* type noop */ /* Empty node, nothing to print, mark, optimize or code */ /* type string */ void print_type_string(NODE *node, int level) { print_level(level); printf("STRING: \""); print_quoted_string(node->v.literal->text); printf("\"\n"); } void mark_type_string(NODE *node) { node->v.literal->flags |= VAR_REFERENCED; } void code_type_string(NODE *node, struct locus **old_locus) { MARK_LOCUS(); code_op(opcode_push); code_immediate((const void*)node->v.literal->off); } /* type symbol */ void print_type_symbol(NODE *node, int level) { print_level(level); printf("SYMBOL: %s\n", node->v.literal->text); } void code_type_symbol(NODE *node, struct locus **old_locus) { MARK_LOCUS(); code_op(opcode_symbol); code_immediate((void*)node->v.literal->off); } void mark_type_symbol(NODE *node) { node->v.literal->flags |= VAR_REFERENCED; } /* type number */ void print_type_number(NODE *node, int level) { print_level(level); printf("NUMBER: %ld\n", node->v.number); } void code_type_number(NODE *node, struct locus **old_locus) { MARK_LOCUS(); code_op(opcode_push); code_immediate((void*)node->v.number); } /* type if */ void print_type_if(NODE *node, int level) { print_level(level); printf("COND: \n"); print_node(node->v.cond.cond, level); print_level(level); printf("IFTRUE\n"); print_node_list(node->v.cond.if_true, level+1); print_level(level); printf("IFFALSE\n"); print_node_list(node->v.cond.if_false, level+1); } void mark_type_if(NODE *node) { mark(node->v.cond.cond); mark(node->v.cond.if_true); mark(node->v.cond.if_false); } void optimize_type_if(NODE *node) { NODE *p = node->v.cond.cond; optimize(p); optimize(node->v.cond.if_true); optimize(node->v.cond.if_false); if (p->type == node_type_number) { NODE *head; NODE *tail = node->next; if (p->v.number) { head = node->v.cond.if_true; free_subtree(node->v.cond.if_false); } else { head = node->v.cond.if_false; free_subtree(node->v.cond.if_true); } if (head) { *node = *head; free_node(head); for (; node->next; node = node->next) ; node->next = tail; } else node->type = node_type_noop; } else if (p->type == node_type_string) { NODE *head; NODE *tail = p->next; if (p->v.literal->text[0]) { head = node->v.cond.if_true; free_subtree(node->v.cond.if_false); } else { head = node->v.cond.if_false; free_subtree(node->v.cond.if_true); } if (head) { *node = *head; free_node(head); for (; node->next; node = node->next) ; node->next = tail; } else node->type = node_type_noop; } } void code_type_if(NODE *node, struct locus **old_locus) { prog_counter_t pos1, pos2, endpos; code_node(node->v.cond.cond); MARK_LOCUS(); code_op(opcode_bz); pos1 = code_immediate(NULL); traverse_tree(node->v.cond.if_true); if (node->v.cond.if_false) { code_op(opcode_jmp); pos2 = code_immediate(NULL); traverse_tree(node->v.cond.if_false); endpos = code_get_counter (); code_put(pos1, (void *)(pos2 - pos1)); code_put(pos2, (void *)(endpos - pos2 - 1)); } else code_put(pos1, (void *)(code_get_counter () - pos1 - 1)); } /* type bin */ void print_type_bin(NODE *node, int level) { print_level(level); print_bin_op(node->v.bin.opcode); if (node->v.bin.opcode == bin_match || node->v.bin.opcode == bin_fnmatch) { if (node->v.bin.qualifier & QUALIFIER_MX) printf(",MX"); } printf("\n"); print_node(node->v.bin.arg[0], level+1); print_node(node->v.bin.arg[1], level+1); } void mark_type_bin(NODE *node) { mark(node->v.bin.arg[0]); mark(node->v.bin.arg[1]); } static void optimize_arith(NODE *node) { NODE *arg0 = node->v.bin.arg[0]; NODE *arg1 = node->v.bin.arg[1]; if (arg0->type == node_type_number && arg1->type == node_type_number) { switch (node->v.bin.opcode) { case bin_add: node->v.number = arg0->v.number + arg1->v.number; break; case bin_sub: node->v.number = arg0->v.number - arg1->v.number; break; case bin_mul: node->v.number = arg0->v.number * arg1->v.number; break; case bin_div: if (arg1->v.number == 0) { parse_error_locus(&node->locus, _("division by zero")); break; } node->v.number = arg0->v.number / arg1->v.number; break; case bin_logand: node->v.number = arg0->v.number & arg1->v.number; break; case bin_logor: node->v.number = arg0->v.number | arg1->v.number; break; case bin_logxor: node->v.number = arg0->v.number ^ arg1->v.number; break; default: return; } node->type = node_type_number; free_node(arg0); free_node(arg1); } else if (node_type(arg0) != dtype_number) { parse_error_locus(&arg0->locus, _("left-hand side argument to the " "arithmetical operation is " "of wrong data type")); } else if (node_type(arg1) != dtype_number) { parse_error_locus(&arg1->locus, _("right-hand side argument to the " "arithmetical operation is " "of wrong data type")); } else if (arg0->type == node_type_number) { switch (node->v.bin.opcode) { case bin_add: if (arg0->v.number == 0) { copy_node(node, arg1); free_node(arg0); free(arg1); } break; case bin_sub: if (arg0->v.number == 0) { NODE *n = alloc_node(node_type_un, &node->locus); n->v.un.opcode = unary_minus; n->v.un.arg = arg1; copy_node(node, n); free_node(arg0); free(n); } break; case bin_mul: if (arg0->v.number == 0) { node->type = node_type_number; node->v.number = 0; free_node(arg0); free_node(arg1); } else if (arg0->v.number == 1) { copy_node(node, arg1); free_node(arg0); } break; case bin_div: if (arg0->v.number == 0) { node->type = node_type_number; node->v.number = 0; free_node(arg0); free_node(arg1); } break; case bin_logand: if (arg0->v.number == 0) { node->type = node_type_number; node->v.number = 0; free_node(arg0); free_node(arg1); } else if (arg0->v.number == ~(unsigned long)0) { copy_node(node, arg1); free_node(arg0); } break; case bin_logor: if (arg0->v.number == 0) { copy_node(node, arg1); free_node(arg0); } else if (arg0->v.number == ~(unsigned long)0) { node->type = node_type_number; node->v.number = ~(unsigned long)0; free_node(arg0); free_node(arg1); } break; case bin_logxor: if (arg0->v.number == 0) { copy_node(node, arg1); free_node(arg0); } break; default: return; } } else if (arg1->type == node_type_number) { switch (node->v.bin.opcode) { case bin_add: case bin_sub: if (arg1->v.number == 0) { copy_node(node, arg0); free_node(arg1); free(arg0); } break; case bin_mul: if (arg1->v.number == 0) { node->type = node_type_number; node->v.number = 0; free_node(arg0); free_node(arg1); } else if (arg1->v.number == 1) { copy_node(node, arg0); free_node(arg1); } break; case bin_div: if (arg1->v.number == 0) { parse_error_locus(&node->locus, _("division by zero")); } else if (arg1->v.number == 1) { copy_node(node, arg0); free_node(arg1); } break; case bin_logand: if (arg1->v.number == 0) { node->type = node_type_number; node->v.number = 0; free_node(arg0); free_node(arg1); } else if (arg1->v.number == ~(unsigned long)0) { copy_node(node, arg0); free_node(arg1); } break; case bin_logor: if (arg1->v.number == 0) { copy_node(node, arg0); free_node(arg1); } else if (arg1->v.number == ~(unsigned long)0) { node->type = node_type_number; node->v.number = ~(unsigned long)0; free_node(arg0); free_node(arg1); } break; case bin_logxor: if (arg1->v.number == 0) { copy_node(node, arg0); free_node(arg0); } break; default: return; } } } void optimize_relational(NODE *node) { NODE *arg0 = node->v.bin.arg[0]; NODE *arg1 = node->v.bin.arg[1]; if (arg0->type == node_type_number && arg1->type == node_type_number) { switch (node->v.bin.opcode) { case bin_and: node->v.number = arg0->v.number && arg1->v.number; break; case bin_or: node->v.number = arg0->v.number || arg1->v.number; break; case bin_eq: node->v.number = arg0->v.number == arg1->v.number; break; case bin_ne: node->v.number = arg0->v.number != arg1->v.number; break; case bin_lt: node->v.number = arg0->v.number < arg1->v.number; break; case bin_le: node->v.number = arg0->v.number <= arg1->v.number; break; case bin_gt: node->v.number = arg0->v.number > arg1->v.number; break; case bin_ge: node->v.number = arg0->v.number >= arg1->v.number; break; default: return; } } else if (arg0->type == node_type_string && arg1->type == node_type_string) { switch (node->v.bin.opcode) { case bin_and: node->v.number = arg0->v.literal->text[0] != 0 && arg1->v.literal->text[0] != 0; break; case bin_or: node->v.number = arg0->v.literal->text[0] != 0 || arg1->v.literal->text[0] != 0; break; case bin_eq: /* NOTE: This case and the one below make use of the fact that no two entries in the symbol table can contain lexicographically equal literals */ node->v.number = arg0->v.literal == arg1->v.literal; break; case bin_ne: node->v.number = arg0->v.literal != arg1->v.literal; break; case bin_lt: node->v.number = strcmp(arg0->v.literal->text, arg1->v.literal->text) < 0; break; case bin_le: node->v.number = strcmp(arg0->v.literal->text, arg1->v.literal->text) <= 0; break; case bin_gt: node->v.number = strcmp(arg0->v.literal->text, arg1->v.literal->text) > 0; break; case bin_ge: node->v.number = strcmp(arg0->v.literal->text, arg1->v.literal->text) >= 0; break; default: return; } } else return; node->type = node_type_number; free_node(arg0); free_node(arg1); } static int node_regmatch(NODE *node, struct literal *lit) { regex_t re; struct sym_regex *sym = node->v.regex; int rc; rc = regcomp(&re, sym->lit->text, sym->regflags); if (rc) { char errbuf[512]; regerror(rc, &re, errbuf, sizeof(errbuf)); parse_error_locus(&node->locus, _("Cannot compile regex: %s"), errbuf); return 1; } rc = regexec(&re, lit->text, 0, NULL, 0); regfree(&re); return rc; } void optimize_type_bin(NODE *node) { NODE *arg0, *arg1; arg0 = node->v.bin.arg[0]; arg1 = node->v.bin.arg[1]; optimize(arg0); optimize(arg1); switch (node->v.bin.opcode) { case bin_and: case bin_or: case bin_eq: case bin_ne: case bin_lt: case bin_le: case bin_gt: case bin_ge: optimize_relational(node); break; case bin_add: case bin_sub: case bin_mul: case bin_div: case bin_logand: case bin_logor: case bin_logxor: optimize_arith(node); break; case bin_match: if (node_type(arg0) != dtype_string) { parse_error_locus(&arg0->locus, _("left-hand side argument " "to match is " "of wrong data type")); } else if (arg1->type == node_type_regex) { if (arg0->type == node_type_string) { node->v.number = node_regmatch(arg1, arg0->v.literal) == 0; node->type = node_type_number; free_node(arg0); free_node(arg1); } } else if (arg1->type != node_type_regcomp) { parse_error_locus(&arg1->locus, _("right-hand side argument " "to match is " "of wrong data type " "(should not happen)")); } break; case bin_fnmatch: if (arg0->type == node_type_string && arg1->type == node_type_string) { node->v.number = fnmatch(arg1->v.literal->text, arg0->v.literal->text, 0) == 0; node->type = node_type_number; free_node(arg0); free_node(arg1); } else if (node_type(arg0) != dtype_string) { parse_error_locus(&arg0->locus, _("left-hand side argument " "to fnmatch is of wrong data type")); } else if (node_type(arg1) != dtype_string) { parse_error_locus(&arg1->locus, _("right-hand side argument " "to fnmatch is of wrong data type")); } break; } } #define __code_cat3__(a,b,c) a ## b ## c #define CODE_BINARY(op, node) do { \ switch (node_type(node->v.bin.arg[0])) { \ case dtype_number: \ code_op(__code_cat3__(opcode_,op,n)); \ break; \ case dtype_string: \ code_op(__code_cat3__(opcode_,op,s)); \ break; \ default: \ parse_error_locus(&node->locus, \ _("Invalid argument type in binary operation")); \ break; \ } \ } while (0) void code_type_bin(NODE *node, struct locus **old_locus) { prog_counter_t pos1, pos2; code_node(node->v.bin.arg[0]); switch (node->v.bin.opcode) { case bin_and: MARK_LOCUS(); /* cond1 if not true goto X cond2 if true goto Y X: push 0 goto Z Y: push 1 Z: */ code_op(opcode_bz); pos1 = code_immediate(NULL); code_node(node->v.bin.arg[1]); code_op(opcode_bnz); pos2 = code_immediate((void *)4); code_op(opcode_push); code_immediate((void *)0); code_op(opcode_jmp); code_immediate((void *)2); code_op(opcode_push); code_immediate((void*)1); code_put(pos1, (void *)(pos2 - pos1)); break; case bin_or: MARK_LOCUS(); /* cond1 if true goto X cond2 if not true goto Y X: push 1 goto Z Y: push 0 Z: */ code_op(opcode_bnz); pos1 = code_immediate(NULL); code_node(node->v.bin.arg[1]); code_op(opcode_bz); pos2 = code_immediate((void *)4); code_op(opcode_push); code_immediate((void *)1); code_op(opcode_jmp); code_immediate((void *)2); code_op(opcode_push); code_immediate((void *)0); code_put(pos1, (void *)(pos2 - pos1)); break; case bin_eq: code_node(node->v.bin.arg[1]); CODE_BINARY(eq, node); break; case bin_ne: code_node(node->v.bin.arg[1]); CODE_BINARY(ne, node); break; case bin_lt: code_node(node->v.bin.arg[1]); CODE_BINARY(lt, node); break; case bin_le: code_node(node->v.bin.arg[1]); CODE_BINARY(le, node); break; case bin_gt: code_node(node->v.bin.arg[1]); CODE_BINARY(gt, node); break; case bin_ge: code_node(node->v.bin.arg[1]); CODE_BINARY(ge, node); break; case bin_match: code_node(node->v.bin.arg[1]); MARK_LOCUS(); if (node->v.bin.qualifier & QUALIFIER_MX) code_op(opcode_regmatch_mx); else code_op(opcode_regmatch); break; case bin_fnmatch: code_node(node->v.bin.arg[1]); MARK_LOCUS(); if (node->v.bin.qualifier & QUALIFIER_MX) code_op(opcode_fnmatch_mx); else code_op(opcode_fnmatch); break; case bin_add: code_node(node->v.bin.arg[1]); MARK_LOCUS(); code_op(opcode_add); break; case bin_sub: code_node(node->v.bin.arg[1]); MARK_LOCUS(); code_op(opcode_sub); break; case bin_mul: code_node(node->v.bin.arg[1]); MARK_LOCUS(); code_op(opcode_mul); break; case bin_div: code_node(node->v.bin.arg[1]); MARK_LOCUS(); code_op(opcode_div); break; case bin_logand: code_node(node->v.bin.arg[1]); MARK_LOCUS(); code_op(opcode_logand); break; case bin_logor: code_node(node->v.bin.arg[1]); MARK_LOCUS(); code_op(opcode_logor); break; case bin_logxor: code_node(node->v.bin.arg[1]); MARK_LOCUS(); code_op(opcode_logxor); break; default: break; } } /* type un */ void print_type_un(NODE *node, int level) { print_level(level); switch (node->v.un.opcode) { case unary_not: printf("NOT\n"); break; case unary_minus: printf("NEG\n"); break; default: abort(); } print_node(node->v.un.arg, level+1); } void mark_type_un(NODE *node) { mark(node->v.un.arg); } void optimize_type_un(NODE *node) { NODE *p = node->v.un.arg; optimize(p); if (p->type == node_type_number) { switch (node->v.un.opcode) { case unary_not: node->v.number = !p->v.number; break; case unary_minus: node->v.number = -p->v.number; break; case unary_lognot: node->v.number = ~p->v.number; break; } node->type = node_type_number; free_node(p); } } void code_type_un(NODE *node, struct locus **old_locus) { code_node(node->v.un.arg); MARK_LOCUS(); switch (node->v.un.opcode) { case unary_not: code_op(opcode_not); break; case unary_minus: code_op(opcode_neg); break; case unary_lognot: code_op(opcode_lognot); break; default: abort(); } } /* type result */ void print_type_result(NODE *node, int level) { NODE *code, *xcode; code = node->v.ret.code; xcode = node->v.ret.xcode; print_level(level); printf("SET REPLY "); print_stat(node->v.ret.stat); printf("\n"); print_level(level); printf("CODE:\n"); if (code) print_node(code, level+1); print_level(level); printf("XCODE:\n"); if (xcode) print_node(xcode, level+1); print_level(level); printf("MESSAGE:\n"); if (node->v.ret.message) print_node(node->v.ret.message, level+1); printf("\n"); } void mark_type_result(NODE *node) { mark(node->v.ret.code); mark(node->v.ret.xcode); mark(node->v.ret.message); } void optimize_type_result(NODE *node) { optimize(node->v.ret.code); optimize(node->v.ret.xcode); optimize(node->v.ret.message); } static void code_result_arg(NODE *node) { if (node) code_node(node); else { code_op(opcode_push); code_immediate(NULL); } } static NODE * result_argptr(NODE *arg) { if (arg && arg->type == node_type_string && arg->v.literal->text[0] == 0) arg = NULL; return arg; } void code_type_result(NODE *node, struct locus **old_locus) { NODE *code, *xcode; code = result_argptr(node->v.ret.code); xcode = result_argptr(node->v.ret.xcode); switch (node->v.ret.stat) { case SMFIS_REJECT: if (code && code->type == node_type_string && code->v.literal->text[0] != '5') parse_error_locus(&node->locus, _("Reject code should be 5xx")); if (xcode && xcode->type == node_type_string && xcode->v.literal->text[0] != '5') parse_error_locus(&node->locus, _("Reject extended code should be 5.x.x")); break; case SMFIS_TEMPFAIL: if (code && code->type == node_type_string && code->v.literal->text[0] != '4') parse_error_locus(&node->locus, _("Tempfail code should be 4xx")); if (xcode && xcode->type == node_type_string && xcode->v.literal->text[0] != '4') parse_error_locus(&node->locus, _("Tempfail extended code should be 4.x.x")); break; default: break; } code_result_arg(node->v.ret.message); code_result_arg(xcode); code_result_arg(code); MARK_LOCUS(); code_op(opcode_result); code_immediate((void*)node->v.ret.stat); code_op(opcode_nil); } /* type header */ void print_type_header(NODE *node, int level) { print_level(level); printf("%s %s: \n", msgmod_opcode_str(node->v.hdr.opcode), node->v.hdr.name->text); print_node_list(node->v.hdr.value, level+1); } void mark_type_header(NODE *node) { node->v.hdr.name->flags |= VAR_REFERENCED; mark(node->v.hdr.value); } void optimize_type_header(NODE *node) { optimize(node->v.hdr.value); } void code_type_header(NODE *node, struct locus **old_locus) { MARK_LOCUS(); if (node->v.hdr.value) code_node(node->v.hdr.value); else { code_op(opcode_push); code_immediate(0); } code_op(opcode_header); code_immediate((void*)node->v.hdr.opcode); code_immediate((void*)node->v.hdr.name->off); } /* type builtin */ void print_type_builtin(NODE *node, int level) { print_level(level); printf("BUILTIN %s\n", node->v.builtin.builtin->name); print_node_list_reverse(node->v.builtin.args, level+1); } void mark_type_builtin(NODE *node) { unsigned i; NODE *p; struct literal *s = literal_lookup(node->v.builtin.builtin->name); s->flags |= VAR_REFERENCED; for (i = 0, p = node->v.builtin.args; p; i++, p = p->next) mark(p); } void optimize_type_builtin(NODE *node) { size_t i; NODE *p; for (i = 0, p = node->v.builtin.args; p; i++, p = p->next) optimize(p); if (strcmp(node->v.builtin.builtin->name, "interval") == 0) { if (node->v.builtin.args->type == node_type_string) { time_t t; const char *endp; if (parse_time_interval( node->v.builtin.args->v.literal->text, &t, &endp)) { parse_error_locus(&node->locus, _("Unrecognized time format (near `%s')"), endp); return; } /* Replace this node */ node->type = node_type_number; node->v.number = t; } } } void code_type_builtin(NODE *node, struct locus **old_locus) { NODE *p; int i; const struct builtin *bp = node->v.builtin.builtin; struct literal *s; /* Pass arguments */ for (p = node->v.builtin.args, i = 0; p; p = p->next, i++) code_node(p); if (bp->optcount || (bp->flags & MFD_BUILTIN_VARIADIC)) { /* Pass the number of actual arguments in a hidden arg */ code_op(opcode_push); code_immediate((void*)i); } MARK_LOCUS(); code_op(opcode_builtin); s = literal_lookup(node->v.builtin.builtin->name); code_immediate((void*)s->off); code_immediate((void*)node->v.builtin.builtin->handler); } /* type concat */ void print_type_concat(NODE *node, int level) { print_level(level); printf("CONCAT:\n"); print_node(node->v.concat.arg[0], level+1); print_node(node->v.concat.arg[1], level+1); } void mark_type_concat(NODE *node) { mark(node->v.concat.arg[0]); mark(node->v.concat.arg[1]); } void optimize_type_concat(NODE *node) { NODE *arg0, *arg1; optimize(node->v.concat.arg[0]); optimize(node->v.concat.arg[1]); arg0 = node->v.concat.arg[0]; arg1 = node->v.concat.arg[1]; if (arg0->type == node_type_string && arg1->type == node_type_string) { string_begin(); string_add(arg0->v.literal->text, strlen(arg0->v.literal->text)); string_add(arg1->v.literal->text, strlen(arg1->v.literal->text)); node->v.literal = string_finish(); node->type = node_type_string; free_node(arg0); free_node(arg1); } else if (arg0->type == node_type_string && arg0->v.literal->text[0] == 0) { copy_node(node, arg1); free_node(arg0); } else if (arg1->type == node_type_string && arg1->v.literal->text[0] == 0) { copy_node(node, arg0); free_node(arg1); } } void code_type_concat(NODE *node, struct locus **old_locus) { code_node(node->v.concat.arg[0]); code_node(node->v.concat.arg[1]); code_op(opcode_concat); } /* type variable */ void print_type_variable(NODE *node, int level) { print_level(level); printf("VARIABLE %s %s %lu(%lu)\n", storage_class_str(node->v.var_ref.variable->storage_class), node->v.var_ref.variable->name, (unsigned long) node->v.var_ref.nframes, (unsigned long) node->v.var_ref.variable->off); } void mark_type_variable(NODE *node) { if (node->v.var_ref.variable->storage_class == storage_extern) node->v.var_ref.variable->flags |= VAR_REFERENCED; } void code_type_variable(NODE *node, struct locus **old_locus) { MARK_LOCUS(); code_memref(node); code_op(opcode_deref); } /* type asgn */ void print_type_asgn(NODE *node, int level) { print_level(level); printf("SET %s %s %lu(%lu)\n", storage_class_str(node->v.asgn.var->storage_class), node->v.asgn.var->name, (unsigned long) node->v.asgn.nframes, (unsigned long) node->v.asgn.var->off); print_node(node->v.asgn.node, level + 1); } void mark_type_asgn(NODE *node) { /* FIXME: This is overly conservative. First of all, `referenced' does not mean `used', so this can create useless assignments, even if the variable in question is not referenced elsewhere: */ node->v.asgn.var->flags |= VAR_REFERENCED; /* Secondly, the node should be marked only if var is referenced or volatile, but determining this would probably require an extra pass. */ mark(node->v.asgn.node); } void optimize_type_asgn(NODE *node) { optimize(node->v.asgn.node); } void code_type_asgn(NODE *node, struct locus **old_locus) { code_node(node->v.asgn.node); node->v.asgn.var->type = node_type(node->v.asgn.node); code_memref(node); code_op(opcode_asgn); } /* type arg */ void print_type_arg(NODE *node, int level) { print_level(level); printf("ARG %u\n", node->v.arg.number); } void code_argref(NODE *node) { code_op(opcode_memstk); code_immediate((void*)0); code_immediate((void*)(node->v.arg.number + 2)); } void code_type_arg(NODE *node, struct locus **old_locus) { MARK_LOCUS(); code_argref(node); code_op(opcode_deref); } /* type argx */ void print_type_argx(NODE *node, int level) { print_level(level); printf("ARGX\n"); print_node(node->v.argx.node, level + 1); } void mark_type_argx(NODE *node) { mark(node->v.argx.node); } void code_argxref(NODE *node) { code_op(opcode_push); code_immediate((void*)0); code_node(node->v.argx.node); code_op(opcode_push); code_immediate((void*)(node->v.argx.nargs + 2)); code_op(opcode_add); code_op(opcode_xmemstk); } void code_type_argx(NODE *node, struct locus **old_locus) { MARK_LOCUS(); code_argxref(node); code_op(opcode_deref); } void optimize_type_argx(NODE *node) { NODE *argx = node->v.argx.node; optimize(argx); if (argx->type == node_type_number) { node->type = node_type_arg; node->v.arg.number = node->v.argx.nargs + 2; } } /* type vaptr */ void print_type_vaptr(NODE *node, int level) { print_level(level); printf("VAPTR\n"); print_node(node->v.node, level + 1); } void mark_type_vaptr(NODE *node) { mark(node->v.node); } void code_type_vaptr(NODE *node, struct locus **old_locus) { NODE *arg = node->v.node; MARK_LOCUS(); switch (arg->type) { case node_type_variable: code_memref(arg); break; case node_type_arg: code_argref(arg); break; case node_type_argx: code_argxref(arg); break; default: abort(); } } void optimize_type_vaptr(NODE *node) { optimize(node->v.node); } char * regex_flags_to_string(int flags, char *buf, size_t size) { static struct { unsigned flag; char *name; } regflg[] = { { REG_EXTENDED, REG_EXTENDED_NAME }, { REG_ICASE, REG_ICASE_NAME }, { REG_NEWLINE, REG_NEWLINE_NAME } }; char *p; int i; p = buf; size--; for (i = 0; i < NELEMS(regflg) && size > 0; i++) { if (regflg[i].flag & flags) { size_t len = strlen(regflg[i].name); if (p > buf) len++; if (len > size) len = size; if (p > buf) { *p++ = ','; len--; } if (len > 0) { memcpy(p, regflg[i].name, len); p += len; } size -= len; } } *p = 0; return buf; } /* type regex */ void print_type_regex(NODE *node, int level) { char buffer[REGEX_STRING_BUFSIZE]; print_level(level); printf("REGEX (%s) %s\n", regex_flags_to_string(node->v.regex->regflags, buffer, sizeof buffer), node->v.regex->lit->text); } void mark_type_regex(NODE *node) { node->v.regex->lit->flags |= VAR_REFERENCED; } void code_type_regex(NODE *node, struct locus **old_locus) { code_op(opcode_regex); code_immediate((void*)node->v.regex->index); } /* type regcomp */ void print_type_regcomp(NODE *node, int level) { char buffer[REGEX_STRING_BUFSIZE]; print_level(level); printf("REGCOMP %s:\n", regex_flags_to_string(node->v.regcomp_data.flags, buffer, sizeof buffer)); print_node(node->v.regcomp_data.expr, level+1); } void mark_type_regcomp(NODE *node) { mark(node->v.regcomp_data.expr); } void optimize_type_regcomp(NODE *node) { int flags = node->v.regcomp_data.flags; NODE *arg0 = node->v.regcomp_data.expr; optimize(arg0); if (arg0->type == node_type_string) { node->type = node_type_regex; node->locus = arg0->locus; node->v.regex = install_regex(arg0->v.literal, flags); } else { struct sym_regex symreg; memset(&symreg, 0, sizeof symreg); symreg.regflags = flags; symreg.index = 0; register_regex(&symreg); node->v.regcomp_data.regind = symreg.index; } } void code_type_regcomp(NODE *node, struct locus **old_locus) { code_node(node->v.regcomp_data.expr); code_op(opcode_regcomp); code_immediate((void*)node->v.regcomp_data.regind); } /* type catch */ void print_type_catch(NODE *node, int level) { int i; print_level(level); printf("CATCH "); for (i = 0; i < mf_exception_count; i++) if (node->v.catch.exmask & EXMASK(i)) printf("%s ", mf_exception_str(i)); printf("\n"); print_node_list(node->v.catch.node, level+1); printf("END CATCH\n"); } void mark_type_catch(NODE *node) { mark(node->v.catch.node); } void optimize_type_catch(NODE *node) { optimize(node->v.catch.node); } void code_type_catch(NODE *node, struct locus **old_locus) { prog_counter_t pos1, endpos; prog_counter_t ctr; MARK_LOCUS(); code_op(opcode_catch); pos1 = code_immediate(NULL); code_immediate((void*)node->v.catch.count); code_immediate((void*)node->v.catch.exmask); ctr = jump_pc; jump_pc = 0; traverse_tree(node->v.catch.node); jump_fixup(jump_pc, code_get_counter()); jump_pc = ctr; code_op(opcode_retcatch); if (node->v.catch.context == context_function) { code_op(opcode_adjust); code_immediate((void*)1); code_op(opcode_jmp); jump_pc = code_immediate((void*)jump_pc); } else { code_result_arg(NULL); code_result_arg(NULL); code_result_arg(NULL); code_op(opcode_result); code_immediate(SMFIS_CONTINUE); } endpos = code_get_counter (); code_put(pos1, (void *)(endpos - pos1)); } /* type throw */ void print_type_throw(NODE *node, int level) { print_level(level); printf("THROW %s\n", mf_exception_str(node->v.throw.code)); print_node(node->v.throw.expr, level+1); } void mark_type_throw(NODE *node) { mark(node->v.throw.expr); } void optimize_type_throw(NODE *node) { optimize(node->v.throw.expr); } void code_type_throw(NODE *node, struct locus **old_locus) { code_node(node->v.throw.expr); MARK_LOCUS(); code_op(opcode_throw); code_immediate((void*)node->v.throw.code); } /* type echo */ void print_type_echo(NODE *node, int level) { print_level(level); printf("ECHO:\n"); print_node(node->v.node, level+1); } void mark_type_echo(NODE *node) { mark(node->v.node); } void optimize_type_echo(NODE *node) { optimize(node->v.node); } void code_type_echo(NODE *node, struct locus **old_locus) { code_node(node->v.node); MARK_LOCUS(); code_op(opcode_echo); } /* type return */ void print_type_return(NODE *node, int level) { print_level(level); if (node->v.node) { printf("RETURN:\n"); print_node(node->v.node, level+1); } } void mark_type_return(NODE *node) { mark(node->v.node); } void optimize_type_return(NODE *node) { optimize(node->v.node); } void code_type_return(NODE *node, struct locus **old_locus) { if (func->rettype == dtype_unspecified) { MARK_LOCUS(); code_op(opcode_jmp); jump_pc = code_immediate((void*)jump_pc); } else { code_node(node->v.node); MARK_LOCUS(); code_op(opcode_popreg); code_op(opcode_jmp); jump_pc = code_immediate((void*)jump_pc); } } /* type call */ void print_type_call(NODE *node, int level) { print_level(level); printf("CALL %s\n", node->v.call.func->name); print_node_list_reverse(node->v.call.args, level+1); } void mark_type_call(NODE *node) { NODE *p; struct literal *s = literal_lookup(node->v.call.func->name); s->flags |= VAR_REFERENCED; for (p = node->v.call.args; p; p = p->next) mark(p); } void optimize_type_call(NODE *node) { NODE *p; for (p = node->v.call.args; p; p = p->next) optimize(p); } void code_type_call(NODE *node, struct locus **old_locus) { NODE *p; struct literal *s; struct function *func = node->v.call.func; int i; if (func->optcount || func->varargs) { int j; /* Count actual arguments */ for (p = node->v.call.args, i = 0; p; p = p->next, i++) ; /* Provide placeholders for the missing ones. This is necessary in order to make assignments to optional arguments possible. */ for (j = i; j < func->parmcount; j++) { code_op(opcode_push); code_immediate(0); } /* Pass actual arguments */ for (p = node->v.call.args; p; p = p->next) code_node(p); /* Pass the number of actual arguments in a hidden arg */ code_op(opcode_push); code_immediate((void*)i); } else for (p = node->v.call.args, i = 0; p; p = p->next, i++) code_node(p); MARK_LOCUS(); code_op(opcode_funcall); s = literal_lookup(func->name); code_immediate((void*)s->off); code_immediate((void*)func->entry); code_op(opcode_adjust); code_immediate((void*)(func->parmcount + (func->optcount ? 1 : 0))); if (func->rettype != dtype_unspecified) code_op(opcode_pushreg); } /* type switch */ void print_type_switch(NODE *node, int level) { struct case_stmt *pcase; print_level(level); printf("SWITCH: \n"); print_node(node->v.switch_stmt.node, level+1); for (pcase = node->v.switch_stmt.cases; pcase; pcase = pcase->next) { print_level(level+1); if (pcase->valist) { struct valist *vp; printf("CASE "); for (vp = pcase->valist; vp; vp = vp->next) switch (vp->value.type) { case dtype_string: printf("\"%s\" ", vp->value.v.literal->text); break; case dtype_number: printf("%ld ", vp->value.v.number); break; default: abort(); } } else { printf("DEFAULT"); } putchar('\n'); print_level(level+1); printf("ACTION\n"); print_node_list(pcase->node, level+2); } } void mark_type_switch(NODE *node) { struct case_stmt *cs; data_type_t type = node_type(node->v.switch_stmt.node); size_t tabsize = 0; record_switch(&node->v.switch_stmt); mark(node->v.switch_stmt.node); for (cs = node->v.switch_stmt.cases; cs; cs = cs->next) { struct valist *vp; mark(cs->node); for (vp = cs->valist; vp; vp = vp->next) { tabsize += 2; if (vp->value.type != type) { char buf[NUMERIC_BUFSIZE_BOUND]; long v; char *p; /* FIXME: The following code is very crude */ switch (type) { case dtype_number: v = strtol(vp->value.v.literal->text, &p, 0); if (*p) { parse_error_locus(&cs->locus, _("cannot convert %s to number"), vp->value.v.literal->text); return; } vp->value.v.number = v; break; case dtype_string: snprintf(buf, sizeof buf, "%ld", vp->value.v.number); vp->value.v.literal = string_alloc(buf, strlen(buf)); break; default: abort(); } vp->value.type = type; } if (vp->value.type == dtype_string) vp->value.v.literal->flags |= VAR_REFERENCED; } } node->v.switch_stmt.tabsize = tabsize; } void optimize_type_switch(NODE *node) { struct case_stmt *pcase; data_type_t type; NODE *p, *bp = NULL; p = node->v.switch_stmt.node; optimize(p); type = node_type(p); for (pcase = node->v.switch_stmt.cases; pcase; pcase = pcase->next) { struct valist *vp; long v; char *s; for (vp = pcase->valist; vp; vp = vp->next) { if (vp->value.type != type) { char buf[NUMERIC_BUFSIZE_BOUND]; /* FIXME: The following code needs generalization */ switch (type) { case dtype_number: v = strtol(vp->value.v.literal->text, &s, 0); if (*s) { parse_error_locus(&pcase->locus, _("cannot convert %s to number"), vp->value.v.literal->text); return; } vp->value.v.number = v; break; case dtype_string: snprintf(buf, sizeof buf, "%ld", vp->value.v.number); vp->value.v.literal = string_alloc(buf, strlen(buf)); break; default: abort(); } } vp->value.type = type; } optimize(pcase->node); } if (p->type == node_type_number || p->type == node_type_string) { for (pcase = node->v.switch_stmt.cases; pcase; pcase = pcase->next) { struct valist *vp; for (vp = pcase->valist; vp; vp = vp->next) { if (p->type == node_type_number ? (vp->value.v.number == p->v.number) /* FIXME: Make sure this works. It is instead of comparing literal string values: */ : (vp->value.v.literal == p->v.literal)) { bp = pcase->node; pcase->node = NULL; break; } } } /* If no node found, use the default one */ if (!bp) { bp = node->v.switch_stmt.cases->node; node->v.switch_stmt.cases->node = NULL; } if (bp) { NODE *tail = node->next; *node = *bp; free_node(bp); for (; node->next; node = node->next) ; node->next = tail; /* FIXME : free branches */ } } } static void jump_fixup(prog_counter_t pos, prog_counter_t endpos) { while (pos) { prog_counter_t next = (prog_counter_t)code_peek(pos); code_put(pos, (void*)(endpos - pos - 1)); pos = next; } } /* The following code is generated for switch statements: popreg ; Pop the result of the previous instruction into the reg xlat ; Look up in the table and replace reg with the new value ; If the switch selector is of string type, xlats is ; coded instead N ; Number of elements in the xlat table OFF ; Offset of the table in the data segment bnz L1 ; If xlat failed, jump to the default case L0: jreg ; Jump to the selected branch L1: ... ; Default case L0+off1: ... ; First branch jmp L2 L0+off2: ... ; Second branch jmp L2 . . . L0+offN: ... ; Nth branch L2: */ static void code_switch_branches(NODE *node, data_type_t type) { prog_counter_t start, refpos, jmppos; struct case_stmt *pcase; size_t data_off = node->v.switch_stmt.off; code_op(opcode_popreg); code_op(type == dtype_number ? opcode_xlat : opcode_xlats); start = code_immediate((void*)node->v.switch_stmt.tabsize); code_immediate((void*) data_off); code_op(opcode_bnz); code_immediate((void*)1); refpos = code_op(opcode_jreg); /* Generate code for the branches */ jmppos = 0; for (pcase = node->v.switch_stmt.cases; pcase; pcase = pcase->next) { struct valist *vp; prog_counter_t pos; pos = code_get_counter() - refpos; traverse_tree(pcase->node); if (pcase->next) { code_op(opcode_jmp); jmppos = code_immediate((void*)jmppos); } for (vp = pcase->valist; vp; vp = vp->next) { switch (type) { case dtype_number: dataseg[data_off] = (STKVAL) vp->value.v.number; break; case dtype_string: dataseg[data_off] = (STKVAL) vp->value.v.literal->off; break; default: abort(); } dataseg[data_off + 1] = (STKVAL) pos; data_off += 2; } } /* Fix up jump offsets */ jump_fixup(jmppos, code_get_counter()); } void code_type_switch(NODE *node, struct locus **old_locus) { code_node(node->v.switch_stmt.node); code_switch_branches(node, node_type(node->v.switch_stmt.node)); } /* Loop nesting stack */ struct loop_stack { struct literal *ident; prog_counter_t *begjmp; prog_counter_t *endjmp; }; static mf_stack_t loop_stack; static int _find_loop(void *item, void *data) { struct loop_stack *ent = item; struct loop_stack *s = data; if (ent->ident == s->ident) { *s = *ent; return 1; } return 0; } static int find_loop(struct literal *lit, struct loop_stack *pret) { if (!loop_stack) return 0; else if (!lit) return mf_stack_peek(loop_stack, 0, pret) == 0; else { struct loop_stack ent; if (!pret) pret = &ent; pret->ident = lit; return mf_stack_enumerate_desc(loop_stack, _find_loop, pret); } } int within_loop(struct literal *lit) { return find_loop(lit, NULL); } void enter_loop(struct literal *lit, prog_counter_t *begptr, prog_counter_t *endptr) { struct loop_stack ent; if (!loop_stack) loop_stack = mf_stack_create(sizeof(struct loop_stack), 0); ent.begjmp = begptr; ent.endjmp = endptr; ent.ident = lit; mf_stack_push(loop_stack, &ent); } void leave_loop() { mf_stack_pop(loop_stack, NULL); } /* type next */ void print_type_next(NODE *node, int level) { print_level(level); printf("NEXT"); if (node->v.literal) printf(" %s", node->v.literal->text); printf("\n"); } void code_type_next(NODE *node, struct locus **old_locus) { struct loop_stack ent; MARK_LOCUS(); if (find_loop(node->v.literal, &ent) == 0) { parse_error_locus(&node->locus, _("INTERNAL ERROR at %s:%d: cannot find loop"), __FILE__, __LINE__); abort(); } code_op(opcode_jmp); *ent.begjmp = code_immediate((void*) *ent.begjmp); } /* type break */ void print_type_break(NODE *node, int level) { print_level(level); printf("BREAK"); if (node->v.literal) printf(" %s", node->v.literal->text); printf("\n"); } void code_type_break(NODE *node, struct locus **old_locus) { struct loop_stack ent; MARK_LOCUS(); if (find_loop(node->v.literal, &ent) == 0) { parse_error_locus(&node->locus, _("INTERNAL ERROR at %s:%d: cannot find loop"), __FILE__, __LINE__); abort(); } code_op(opcode_jmp); *ent.endjmp = code_immediate((void*) *ent.endjmp); } /* type loop */ void print_type_loop(NODE *node, int level) { print_level(level); printf("LOOP"); if (node->v.loop.ident) printf(" %s", node->v.loop.ident->text); printf(":\n"); if (node->v.loop.for_stmt) { print_level(level); printf("FOR "); print_node_list(node->v.loop.for_stmt, level + 1); } if (node->v.loop.beg_while) { print_level(level); printf("BEG_WHILE "); print_node_list(node->v.loop.beg_while, level + 1); } if (node->v.loop.end_while) { print_level(level); printf("BEG_WHILE "); print_node_list(node->v.loop.end_while, level + 1); } if (node->v.loop.stmt) { print_level(level); printf("BY "); print_node_list(node->v.loop.stmt, level + 1); } print_level(level); print_node_list(node->v.loop.body, level + 1); } void mark_type_loop(NODE *node) { mark(node->v.loop.beg_while); mark(node->v.loop.stmt); mark(node->v.loop.for_stmt); mark(node->v.loop.end_while); mark(node->v.loop.body); } void optimize_type_loop(NODE *node) { optimize(node->v.loop.beg_while); if (node->v.loop.beg_while) { if (node->v.loop.beg_while->type == node_type_number) { if (node->v.loop.beg_while->v.number) node->v.loop.beg_while = NULL; else node->type = node_type_noop; /* FIXME: free subtrees?? */ return; } else if (node->v.loop.beg_while->type == node_type_string) { if (node->v.loop.beg_while->v.literal->text[0]) node->v.loop.beg_while = NULL; else node->type = node_type_noop; return; } } optimize(node->v.loop.stmt); optimize(node->v.loop.for_stmt); optimize(node->v.loop.end_while); optimize(node->v.loop.body); } void code_type_loop(NODE *node, struct locus **old_locus) { /* FIXME */ /* . . . L_begin: [ bz L_end] [ bz L_end] [] jmp L_begin L_end: */ prog_counter_t begin, end, begjmp = 0, endjmp = 0; MARK_LOCUS(); enter_loop(node->v.loop.ident, &begjmp, &endjmp); traverse_tree(node->v.loop.for_stmt); begin = code_get_counter(); if (node->v.loop.beg_while) { code_node(node->v.loop.beg_while); code_op(opcode_bz); endjmp = code_immediate((void*)endjmp); } traverse_tree(node->v.loop.body); if (node->v.loop.end_while) { code_node(node->v.loop.end_while); code_op(opcode_bz); endjmp = code_immediate((void*)endjmp); } traverse_tree(node->v.loop.stmt); code_op(opcode_jmp); code_immediate((void*)(begin - code_get_counter() - 1)); end = code_get_counter(); jump_fixup(begjmp, begin); jump_fixup(endjmp, end); } /* type backref */ void print_type_backref(NODE *node, int level) { print_level(level); printf("BACKREF: %ld\n", node->v.number); } void code_type_backref(NODE *node, struct locus **old_locus) { MARK_LOCUS(); code_op(opcode_backref); code_immediate((void*)node->v.number); } /* type cast */ void print_type_cast(NODE *node, int level) { print_level(level); printf("CAST %s\n", type_to_string(node->v.cast.data_type)); print_node_list(node->v.cast.node, level+1); } void mark_type_cast(NODE *node) { mark(node->v.cast.node); } void optimize_type_cast(NODE *node) { NODE *p; optimize(node->v.cast.node); p = node->v.cast.node; if (p->type == node_type_string) { node->type = node_type_string; node->v = p->v; free_node(p); } else if (p->type == node_type_number) { char buf[NUMERIC_BUFSIZE_BOUND]; snprintf(buf, sizeof buf, "%ld", p->v.number); node->v.literal = string_alloc(buf, strlen(buf)); node->type = node_type_string; } else if (node->v.cast.data_type == dtype_number && node_type(p) == dtype_number) { node->type = p->type; node->locus = p->locus; node->v = p->v; free_node(p); } } int code_cast(struct locus *locus, data_type_t fromtype, data_type_t totype) { if (fromtype == totype) return 0; switch (fromtype) { case dtype_unspecified: parse_error_locus(locus, _("Expression evaluates to unspecified data type")); return 1; case dtype_string: code_op(opcode_ston); break; case dtype_number: case dtype_pointer: code_op(opcode_ntos); break; } return 0; } void code_type_cast(NODE *node, struct locus **old_locus) { code_node(node->v.cast.node); code_cast(&node->v.cast.node->locus, node_type(node->v.cast.node), node->v.cast.data_type); } /* type funcdecl */ void mark_type_funcdecl(NODE *node) { mark(node->v.funcdecl.tree); } void optimize_type_funcdecl(NODE *node) { optimize(node->v.funcdecl.tree); } void code_type_funcdecl(NODE *node, struct locus **old_locus) { prog_counter_t pc; func = node->v.funcdecl.func; /* Assign the entry point early to properly handle recursive functions */ func->entry = code_get_counter(); codegen(&pc, node->v.funcdecl.tree, func->exmask, 0, node->v.funcdecl.auto_count); } /* type progdecl */ void mark_type_progdecl(NODE *node) { mark(node->v.progdecl.tree); } void optimize_type_progdecl(NODE *node) { optimize(node->v.progdecl.tree); } void code_type_progdecl(NODE *node, struct locus **old_locus) { enum smtp_state tag; tag = node->v.progdecl.tag; if (root_node[tag]) { parse_warning_locus(&node->locus, _("Redefinition of handler `%s'"), state_to_string(tag)); parse_warning_locus(&root_node[tag]->locus, _("This is the location of the previous definition")); } root_node[tag] = node->v.progdecl.tree; if (codegen(&entry_point[tag], node->v.progdecl.tree, 0, 1, node->v.progdecl.auto_count) == 0) milter_enable_state(tag); } /* type offset */ void mark_type_offset(NODE *node) { if (node->v.var_ref.variable->storage_class == storage_extern) node->v.var_ref.variable->flags |= VAR_REFERENCED; } void print_type_offset(NODE *node, int level) { print_level(level); printf("OFFSET OF VARIABLE %s %s %lu(%lu)\n", storage_class_str(node->v.var_ref.variable->storage_class), node->v.var_ref.variable->name, (unsigned long) node->v.var_ref.nframes, (unsigned long) node->v.var_ref.variable->off); } void optimize_type_offset(NODE *node) { node->type = node_type_number; switch (node->v.var_ref.variable->storage_class) { case storage_extern: node->v.number = node->v.var_ref.variable->off; break; case storage_auto: node->v.number = 0; break; case storage_param: node->v.number = node->v.var_ref.variable->ord; } } void code_type_offset(NODE *node, struct locus **old_locus) { MARK_LOCUS(); switch (node->v.var_ref.variable->storage_class) { case storage_extern: code_op(opcode_push); code_immediate((void*)node->v.var_ref.variable->off); break; case storage_auto: code_op(opcode_push); code_immediate((void*)0); break; case storage_param: code_op(opcode_push); code_immediate((void*)node->v.var_ref.variable->ord); } }