From 41e9682efed7cd1df133b1b4ac806e07723f1486 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin (Intel)" Date: Thu, 25 Apr 2019 18:00:32 -0700 Subject: [PATCH] preproc: massive cleanup of smacro expansion The smacro expansion code was virtually impossible to understand, and was leading to very strange failures. Clean it up, and do much better handling of magic macros. This should also allow for recursive macros, but recursive macros are extremely tricky in that it is very hard to keep them from recursing forever, unless there is at least one argument which is never expanded. They are not currently implemented. Even so, I believe token pasting makes it possible to create infinite loops; e.g.: %define foo foo %+ Signed-off-by: H. Peter Anvin (Intel) --- asm/preproc.c | 793 ++++++++++++++++++++++++---------------------- asm/quote.c | 26 +- asm/quote.h | 4 +- include/nasmlib.h | 13 + nasmlib/raa.c | 5 - 5 files changed, 452 insertions(+), 389 deletions(-) diff --git a/asm/preproc.c b/asm/preproc.c index 47a4de20..804ff455 100644 --- a/asm/preproc.c +++ b/asm/preproc.c @@ -95,20 +95,24 @@ typedef struct Cond Cond; * performance issue, you may want to reconsider your coding style. */ +/* + * Function call for a magic smacro + */ +typedef Token *(*MagicSMacro)(const SMacro *s, Token **params, + unsigned int nparams, bool *free_expansion); + /* * Store the definition of a single-line macro. */ struct SMacro { SMacro *next; /* MUST BE FIRST - see free_smacro() */ char *name; - union { - Token *expansion; - Token *(*magic)(const SMacro *s, Token **params, int *paramsize); - } e; + Token *expansion; + MagicSMacro magic; + intorptr magicpvt; bool *eval_param; unsigned int nparam; bool casesense; - bool magic; bool in_progress; }; @@ -210,7 +214,6 @@ enum pp_token_type { TOK_PREPROC_Q, TOK_PREPROC_QQ, TOK_PASTE, /* %+ */ TOK_INDIRECT, /* %[...] */ - TOK_SMAC_END, /* Marker for the end of smacro expansion */ TOK_SMAC_START_PARAMS, /* MUST BE LAST IN THE LIST!!! */ TOK_MAX = INT_MAX /* Keep compiler from reducing the range */ }; @@ -239,10 +242,7 @@ struct tokseq_match { struct Token { Token *next; char *text; - union { - SMacro *mac; /* associated macro for TOK_SMAC_END */ - size_t len; /* scratch length field */ - } a; /* Auxiliary data */ + size_t len; /* scratch length field */ enum pp_token_type type; }; @@ -470,7 +470,8 @@ static vefunc real_verror; static void *new_Block(size_t size); static void delete_Blocks(void); static Token *new_Token(Token * next, enum pp_token_type type, - const char *text, int txtlen); + const char *text, size_t txtlen); +static Token *dup_Token(Token *next, const Token *src); static Token *delete_Token(Token * t); /* @@ -482,19 +483,25 @@ static Token *delete_Token(Token * t); #define tok_isnt_(x,v) ((x) && ((x)->type!=TOK_OTHER || strcmp((x)->text,(v)))) /* - * nasm_unquote with error if the string contains NUL characters. - * If the string contains NUL characters, issue an error and return - * the C len, i.e. truncate at the NUL. + * nasm_unquote with error if the string contains control characters. + * If the string contains control characters, issue an error and return + * the C len, i.e. truncate at the control character. */ static size_t nasm_unquote_cstr(char *qstr, enum preproc_token directive) { size_t len = nasm_unquote(qstr, NULL); - size_t clen = strlen(qstr); + size_t i; - if (len != clen) - nasm_nonfatal("NUL character in `%s' directive", - pp_directives[directive]); - return clen; + for (i = 0; i < len; i++) { + if (qstr[i] < ' ') { + nasm_nonfatal("control character in `%s' directive string", + pp_directives[directive]); + break; + } + } + + qstr[i] = '\0'; + return i; } /* @@ -633,20 +640,29 @@ static void free_mmacro(MMacro * m) } /* - * Free an SMacro + * Clear or free an SMacro */ -static void free_smacro(SMacro *s, bool really) +static void free_smacro_members(SMacro *s) { nasm_free(s->name); - if (!s->magic) - free_tlist(s->e.expansion); + free_tlist(s->expansion); nasm_free(s->eval_param); - if (really) { - nasm_free(s); - } else { - /* Wipe everything except the next pointer */ - memset(&s->next + 1, 0, sizeof *s - sizeof s->next); - } +} + +static void clear_smacro(SMacro *s) +{ + free_smacro_members(s); + /* Wipe everything except the next pointer */ + memset(&s->next + 1, 0, sizeof *s - sizeof s->next); +} + +/* + * Free an SMacro + */ +static void free_smacro(SMacro *s) +{ + free_smacro_members(s); + nasm_free(s); } /* @@ -662,7 +678,7 @@ static void free_smacro_table(struct hash_table *smt) SMacro *s = np->data; nasm_free((void *)np->key); list_for_each_safe(s, tmp, s) - free_smacro(s, true); + free_smacro(s); } hash_free(smt); } @@ -1208,11 +1224,10 @@ static void delete_Blocks(void) /* * this function creates a new Token and passes a pointer to it - * back to the caller. It sets the type and text elements, and - * also the a.mac and next elements to NULL. + * back to the caller. It sets the type, text, and next pointer elements. */ static Token *new_Token(Token * next, enum pp_token_type type, - const char *text, int txtlen) + const char *text, size_t txtlen) { Token *t; int i; @@ -1226,13 +1241,14 @@ static Token *new_Token(Token * next, enum pp_token_type type, t = freeTokens; freeTokens = t->next; t->next = next; - t->a.mac = NULL; t->type = type; if (type == TOK_WHITESPACE || !text) { + t->len = 0; t->text = NULL; } else { - if (txtlen == 0) + if (txtlen == 0 && text[0]) txtlen = strlen(text); + t->len = txtlen; t->text = nasm_malloc(txtlen+1); memcpy(t->text, text, txtlen); t->text[txtlen] = '\0'; @@ -1240,6 +1256,11 @@ static Token *new_Token(Token * next, enum pp_token_type type, return t; } +static Token *dup_Token(Token *next, const Token *src) +{ + return new_Token(next, src->type, src->text, src->len); +} + static Token *delete_Token(Token * t) { Token *next = t->next; @@ -2054,7 +2075,7 @@ static SMacro *define_smacro(Context *ctx, const char *mname, * existing SMacro structure. This means freeing * what was already in it, but not the structure itself. */ - free_smacro(smac, false); + clear_smacro(smac); } } else { smtbl = ctx ? &ctx->localmac : &smacros; @@ -2066,7 +2087,7 @@ static SMacro *define_smacro(Context *ctx, const char *mname, smac->name = nasm_strdup(mname); smac->casesense = casesense; smac->nparam = nparam; - smac->e.expansion = expansion; + smac->expansion = expansion; return smac; } @@ -2089,7 +2110,7 @@ static void undef_smacro(Context *ctx, const char *mname) while ((s = *sp) != NULL) { if (!mstrcmp(s->name, mname, s->casesense)) { *sp = s->next; - free_smacro(s, true); + free_smacro(s); } else { sp = &s->next; } @@ -3176,7 +3197,9 @@ issue_error: while (t) { if (t->type == TOK_ID) { list_for_each(tt, param_start) - if (is_smac_param(tt->type) && !strcmp(tt->text, t->text)) + if (is_smac_param(tt->type) && + tt->len == t->len && + !memcmp(tt->text, t->text, tt->len)) t->type = tt->type; } tt = t->next; @@ -3437,7 +3460,7 @@ issue_error: case TOK_WHITESPACE: break; case TOK_STRING: - len += t->a.len = nasm_unquote(t->text, NULL); + len += t->len = nasm_unquote(t->text, NULL); break; case TOK_OTHER: if (!strcmp(t->text, ",")) /* permit comma separators */ @@ -3454,8 +3477,8 @@ issue_error: p = pp = nasm_malloc(len); list_for_each(t, tline) { if (t->type == TOK_STRING) { - memcpy(p, t->text, t->a.len); - p += t->a.len; + memcpy(p, t->text, t->len); + p += t->len; } } @@ -3556,7 +3579,9 @@ issue_error: start = -1, count = 0; /* empty string */ macro_start = new_Token(NULL, TOK_STRING, NULL, 0); - macro_start->text = nasm_quote((start < 0) ? "" : t->text + start, count); + macro_start->len = count; + macro_start->text = nasm_quote((start < 0) ? "" : t->text + start, + ¯o_start->len); /* * We now have a macro name, an implicit parameter count of @@ -3689,9 +3714,11 @@ static int find_cc(Token * t) } /* - * This routines walks over tokens strem and hadnles tokens + * This routines walks over tokens strem and handles tokens * pasting, if @handle_explicit passed then explicit pasting * term is handled, otherwise -- implicit pastings only. + * The @m array can contain a series of token types which are + * executed as separate passes. */ static bool paste_tokens(Token **head, const struct tokseq_match *m, size_t mnum, bool handle_explicit) @@ -4087,7 +4114,7 @@ static Token *expand_mmac_params(Token * tline) t->type = type; nasm_free(t->text); t->text = text; - t->a.mac = NULL; + t->len = strlen(text); } changed = true; continue; @@ -4099,7 +4126,6 @@ static Token *expand_mmac_params(Token * tline) tt = expand_smacro(tt); *tail = tt; while (tt) { - tt->a.mac = NULL; /* Necessary? */ tail = &tt->next; tt = tt->next; } @@ -4108,7 +4134,6 @@ static Token *expand_mmac_params(Token * tline) } else { t = *tail = tline; tline = tline->next; - t->a.mac = NULL; tail = &t->next; } } @@ -4135,6 +4160,307 @@ static Token *expand_mmac_params(Token * tline) return thead; } +/* + * Expand *one* single-line macro instance. If the first token is not + * a macro at all, it is simply copied to the output. tpp should be + * a pointer to a pointer (usually the next pointer of the previous token) + * to the first token. **tpp is updated to point to the pointer to first token + * of the expansion, which is also the return value, and then *tpp will point to + * the pointer to the first input token after the expansion. + * + * If the expansion is empty, then these two values point to the same token. + * + */ +static Token *expand_smacro_noreset(Token * tline); + +static Token *expand_one_smacro(Token ***tpp) +{ + Token **params = NULL; + const char *mname; + Token *tline = **tpp; + Token **tnextp; + SMacro *head, *m; + unsigned int nparam, i; + Token *expansion; + Token *t, *mstart, **ttail; + bool free_expansion; + + if (!tline) + return NULL; + + mname = tline->text; + tnextp = &tline->next; /* By default we shift out one token */ + + if (tline->type == TOK_ID) { + head = (SMacro *)hash_findix(&smacros, mname); + } else if (tline->type == TOK_PREPROC_ID) { + Context *ctx = get_ctx(mname, &mname); + head = ctx ? (SMacro *)hash_findix(&ctx->localmac, mname) : NULL; + } else { + goto not_a_macro; + } + + /* + * We've hit an identifier of some sort. First check whether the + * identifier is a single-line macro at all, then think about + * checking for parameters if necessary. + */ + list_for_each(m, head) { + if (!mstrcmp(m->name, mname, m->casesense)) + break; + } + + if (!m) { + goto not_a_macro; + } + + /* Parse parameters, if applicable */ + + params = NULL; + nparam = 0; + + if (m->nparam == 0) { + /* + * Simple case: the macro is parameterless. + * Nothing to parse; the expansion code will + * drop the macro name token. + */ + } else { + /* + * Complicated case: at least one macro with this name + * exists and takes parameters. We must find the + * parameters in the call, count them, find the SMacro + * that corresponds to that form of the macro call, and + * substitute for the parameters when we expand. What a + * pain. + */ + Token **pep; + int paren; + int white = 0; + int brackets = 0; + bool bracketed = false; + bool bad_bracket = false; + unsigned int sparam = PARAM_DELTA; + + tline = tline->next; + + while (tok_type_(tline, TOK_WHITESPACE)) { + tline = tline->next; + } + if (!tok_is_(tline, "(")) { + /* + * This macro wasn't called with parameters: ignore + * the call. (Behaviour borrowed from gnu cpp.) + */ + goto not_a_macro; + } + + paren = 1; + nparam = 0; + params = nasm_malloc(sparam * sizeof *params); + params[0] = NULL; + pep = ¶ms[0]; + + while (true) { + bool skip = false; + char ch; + + if (!tline->next) { + nasm_nonfatal("macro call expects terminating `)'"); + break; + } + tline = tline->next; + + ch = (tline->type == TOK_OTHER && !tline->text[1]) + ? tline->text[0] : 0; + + if (!brackets) { + if (tline->type == TOK_WHITESPACE) { + if (params[nparam]) { + /* Keep interior whitespace */ + white++; + } + skip = true; + } + + switch (ch) { + case ',': + if (!paren) { + if (++nparam >= sparam) { + sparam += PARAM_DELTA; + params = nasm_realloc(params, sparam * sizeof *params); + } + params[nparam] = NULL; + pep = ¶ms[nparam]; + white = 0; + bracketed = false; + } + break; + + case '{': + brackets++; + bracketed = skip = !params[nparam]; + break; + + case '(': + paren++; + break; + + case ')': + paren--; + break; + + default: + break; /* Just advance to the next token */ + } + } else { + if (ch == '}') + brackets--; + + if (brackets == 0) + skip = bracketed; + } + + if (!paren) + break; /* We are done */ + + if (!skip) { + Token *t; + + if (bracketed && !brackets) + bad_bracket = true; + + if (white) { + t = new_Token(NULL, TOK_WHITESPACE, NULL, 0); + *pep = t; + pep = &t->next; + } + + t = new_Token(NULL, tline->type, tline->text, 0); + *pep = t; + pep = &t->next; + white = 0; + } + } + nparam++; /* Count the last parameter */ + + if (bad_bracket) + nasm_nonfatal("braces do not enclose all of macro parameter"); + + /* Look for a macro matching in both name and parameter count */ + while (m && (m->nparam != nparam || + mstrcmp(m->name, mname, m->casesense))) + m = m->next; + + if (!m) { + /*! + *!macro-params [on] macro calls with wrong parameter count + *! covers warnings about \i{multi-line macros} being invoked + *! with the wrong number of parameters. See \k{mlmacover} for an + *! example of why you might want to disable this warning. + */ + nasm_warn(WARN_MACRO_PARAMS, + "macro `%s' exists, " + "but not taking %d parameters", + mname, nparam); + goto not_a_macro_cleanup; + } + } + + if (m->in_progress) + goto not_a_macro_cleanup; + + /* Expand each parameter */ + m->in_progress = true; + + for (i = 0; i < nparam; i++) { + params[i] = expand_smacro_noreset(params[i]); + + if (m->eval_param && m->eval_param[i]) { + /* Evaluate this parameter as a number */ + struct ppscan pps; + struct tokenval tokval; + expr *evalresult; + + pps.tptr = params[i]; + pps.ntokens = -1; + tokval.t_type = TOKEN_INVALID; + evalresult = evaluate(ppscan, &pps, &tokval, NULL, true, NULL); + + if (!evalresult) { + /* Nothing meaningful to do */ + } else if (tokval.t_type) { + nasm_nonfatal("invalid expression in parameter %d of macro `%s'", i, m->name); + } else if (!is_simple(evalresult)) { + nasm_nonfatal("non-constant expression in parameter %d of macro `%s'", i, m->name); + } else { + free_tlist(params[i]); + params[i] = make_tok_num(reloc_value(evalresult)); + } + } + + /* Put tokens in reverse order like the expansion list */ + params[i] = reverse_tokens(params[i]); + } + + t = tline; + tline = tline->next; + t->next = NULL; /* Remove the macro call from the input */ + + if (unlikely(m->magic)) { + expansion = m->magic(m, params, nparam, &free_expansion); + } else { + expansion = m->expansion; + free_expansion = false; + } + + ttail = NULL; + + list_for_each(t, expansion) { + if (is_smac_param(t->type)) { + Token *ttt; + list_for_each(ttt, params[smac_nparam(t->type)]) { + tline = dup_Token(tline, ttt); + if (!ttail) + ttail = &tline->next; + } + } else { + if (t->type == TOK_PREPROC_Q) { + tline = new_Token(tline, TOK_ID, mname, 0); + } else if (t->type == TOK_PREPROC_QQ) { + tline = new_Token(tline, TOK_ID, m->name, 0); + } else { + tline = dup_Token(tline, t); + } + if (!ttail) + ttail = &tline->next; + } + } + + if (free_expansion) + free_tlist(expansion); + + m->in_progress = false; + + /* Don't do this until after expansion or we will clobber mname */ + mstart = **tpp; + tnextp = ttail ? ttail : *tpp; + **tpp = tline; + free_tlist(mstart); + + /* + * No macro expansion needed; roll back to mstart (if necessary) + * and then advance to the next input token. + */ +not_a_macro_cleanup: + nasm_free(params); + +not_a_macro: + t = **tpp; + *tpp = tnextp; + return t; +} + /* * Expand all single-line macro calls made in the given line. * Return the expanded version of the line. The original is deemed @@ -4142,20 +4468,17 @@ static Token *expand_mmac_params(Token * tline) * Tokens from input to output a lot of the time, rather than * actually bothering to destroy and replicate.) */ - -static Token *expand_smacro(Token * tline) +static int64_t smacro_deadman; +static Token *expand_smacro(Token *tline) { - Token *t, *tt, *mstart, **tail, *thead; - SMacro *head = NULL, *m; - Token **params; - int *paramsize; - Token *eparams; - unsigned int nparam, sparam, i; - int brackets; + smacro_deadman = nasm_limit[LIMIT_MACROS]; + return expand_smacro_noreset(tline); +} + +static Token *expand_smacro_noreset(Token * tline) +{ + Token *t, **tail, *thead; Token *org_tline = tline; - Context *ctx; - const char *mname; - int64_t deadman = nasm_limit[LIMIT_MACROS]; bool expanded; /* @@ -4167,7 +4490,6 @@ static Token *expand_smacro(Token * tline) if (org_tline) { tline = new_Token(org_tline->next, org_tline->type, org_tline->text, 0); - tline->a.mac = org_tline->a.mac; nasm_free(org_tline->text); org_tline->text = NULL; } @@ -4175,310 +4497,20 @@ static Token *expand_smacro(Token * tline) expanded = true; /* Always expand %+ at least once */ again: - thead = NULL; + thead = tline; tail = &thead; - while (tline) { /* main token loop */ - if (!--deadman) { + while ((t = *tail)) { /* main token loop */ + if (!--smacro_deadman) { nasm_nonfatal("interminable macro recursion"); goto err; } - if ((mname = tline->text)) { - /* if this token is a local macro, look in local context */ - if (tline->type == TOK_ID) { - head = (SMacro *)hash_findix(&smacros, mname); - } else if (tline->type == TOK_PREPROC_ID) { - ctx = get_ctx(mname, &mname); - head = ctx ? (SMacro *)hash_findix(&ctx->localmac, mname) : NULL; - } else - head = NULL; - - /* - * We've hit an identifier. As in is_mmacro below, we first - * check whether the identifier is a single-line macro at - * all, then think about checking for parameters if - * necessary. - */ - list_for_each(m, head) { - if (!mstrcmp(m->name, mname, m->casesense)) - break; - } - if (m) { - mstart = tline; - params = NULL; - paramsize = NULL; - eparams = NULL; - - if (m->nparam == 0) { - /* - * Simple case: the macro is parameterless. - * Nothing to parse; just drop the macro token itself. - */ - tline = tline->next; - } else { - /* - * Complicated case: at least one macro with this name - * exists and takes parameters. We must find the - * parameters in the call, count them, find the SMacro - * that corresponds to that form of the macro call, and - * substitute for the parameters when we expand. What a - * pain. - */ - /*tline = tline->next; - skip_white_(tline); */ - do { - t = tline->next; - while (tok_type_(t, TOK_SMAC_END)) { - if (t->a.mac) - t->a.mac->in_progress = false; - t->text = NULL; - t = tline->next = delete_Token(t); - } - tline = t; - } while (tok_type_(tline, TOK_WHITESPACE)); - if (!tok_is_(tline, "(")) { - /* - * This macro wasn't called with parameters: ignore - * the call. (Behaviour borrowed from gnu cpp.) - */ - tline = mstart; - m = NULL; - } else { - int paren = 0; - int white = 0; - brackets = 0; - nparam = 0; - sparam = PARAM_DELTA; - params = nasm_malloc(sparam * sizeof(Token *)); - params[0] = tline->next; - paramsize = nasm_malloc(sparam * sizeof(int)); - paramsize[0] = 0; - while (true) { /* parameter loop */ - /* - * For some unusual expansions - * which concatenates function call - */ - t = tline->next; - while (tok_type_(t, TOK_SMAC_END)) { - if (t->a.mac) - t->a.mac->in_progress = false; - t->text = NULL; - t = tline->next = delete_Token(t); - } - tline = t; - - if (!tline) { - nasm_nonfatal("macro call expects terminating `)'"); - break; - } - if (tline->type == TOK_WHITESPACE - && brackets <= 0) { - if (paramsize[nparam]) - white++; - else - params[nparam] = tline->next; - continue; /* parameter loop */ - } - if (tline->type == TOK_OTHER - && tline->text[1] == 0) { - char ch = tline->text[0]; - if (ch == ',' && !paren && brackets <= 0) { - if (++nparam >= sparam) { - sparam += PARAM_DELTA; - params = nasm_realloc(params, - sparam * sizeof(Token *)); - paramsize = nasm_realloc(paramsize, - sparam * sizeof(int)); - } - params[nparam] = tline->next; - paramsize[nparam] = 0; - white = 0; - continue; /* parameter loop */ - } - if (ch == '{' && - (brackets > 0 || (brackets == 0 && - !paramsize[nparam]))) - { - if (!(brackets++)) { - params[nparam] = tline->next; - continue; /* parameter loop */ - } - } - if (ch == '}' && brackets > 0) - if (--brackets == 0) { - brackets = -1; - continue; /* parameter loop */ - } - if (ch == '(' && !brackets) - paren++; - if (ch == ')' && brackets <= 0) - if (--paren < 0) - break; - } - if (brackets < 0) { - brackets = 0; - nasm_nonfatal("braces do not " - "enclose all of macro parameter"); - } - paramsize[nparam] += white + 1; - white = 0; - } /* parameter loop */ - nparam++; - - while (m && (m->nparam != nparam || - mstrcmp(m->name, mname, m->casesense))) - m = m->next; - if (!m) { - /*! - *!macro-params [on] macro calls with wrong parameter count - *! covers warnings about \i{multi-line macros} being invoked - *! with the wrong number of parameters. See \k{mlmacover} for an - *! example of why you might want to disable this warning. - */ - nasm_warn(WARN_MACRO_PARAMS, - "macro `%s' exists, " - "but not taking %d parameters", - mstart->text, nparam); - } else if (m->eval_param) { - struct ppscan pps; - struct tokenval tokval; - expr *evalresult; - - /* Evaluate parameters if applicable */ - for (i = 0; i < nparam; i++) { - if (!m->eval_param[i]) - continue; - - pps.tptr = params[i]; - pps.ntokens = paramsize[i]; - tokval.t_type = TOKEN_INVALID; - evalresult = evaluate(ppscan, &pps, &tokval, - NULL, true, NULL); - if (!evalresult) - continue; - - if (tokval.t_type) { - nasm_error(ERR_NONFATAL, - "invalid expression in parameter %d of macro `%s'", i, m->name); - continue; - } - - if (!is_simple(evalresult)) { - nasm_error(ERR_NONFATAL, - "non-constant expression in parameter %d of macro `%s'", i, m->name); - continue; - } - params[i] = make_tok_num(reloc_value(evalresult)); - params[i]->next = eparams; - eparams = params[i]; - paramsize[i] = 1; - } - } - } - } - if (m && m->in_progress) - m = NULL; - if (!m) { - /* in progress or didn't find '(' or wrong nparam */ - /* - * Design question: should we handle !tline, which - * indicates missing ')' here, or expand those - * macros anyway, which requires the (t) test a few - * lines down? - */ - free_tlist(eparams); - nasm_free(params); - nasm_free(paramsize); - tline = mstart; - } else { - /* - * Expand the macro: we are placed on the last token of the - * call, so that we can easily split the call from the - * following tokens. We also start by pushing an SMAC_END - * token for the cycle removal. - */ - Token *expansion; - - t = tline; - if (t) { - tline = t->next; - t->next = NULL; - } - tt = new_Token(tline, TOK_SMAC_END, NULL, 0); - tt->a.mac = m; - m->in_progress = true; - if (unlikely(m->magic)) - expansion = m->e.magic(m, params, paramsize); - else - expansion = m->e.expansion; - - tline = tt; - list_for_each(t, expansion) { - if (is_smac_param(t->type)) { - Token *pcopy = tline, **ptail = &pcopy; - Token *ttt, *pt; - int i; - - ttt = params[smac_nparam(t->type)]; - i = paramsize[smac_nparam(t->type)]; - while (--i >= 0) { - nasm_assert(ttt); - pt = *ptail = new_Token(tline, ttt->type, - ttt->text, 0); - ptail = &pt->next; - ttt = ttt->next; - if (!ttt && i > 0) { - /* - * FIXME: Need to handle more gracefully, - * exiting early on arguments analysis. - */ - nasm_fatal("macro `%s' expects %d args", - mstart->text, - paramsize[t->type - TOK_SMAC_START_PARAMS]); - } - } - tline = pcopy; - } else if (t->type == TOK_PREPROC_Q) { - tt = new_Token(tline, TOK_ID, mname, 0); - tline = tt; - } else if (t->type == TOK_PREPROC_QQ) { - tt = new_Token(tline, TOK_ID, m->name, 0); - tline = tt; - } else { - tt = new_Token(tline, t->type, t->text, 0); - tline = tt; - } - } - - /* - * Having done that, get rid of the macro call, and clean - * up the parameters. - */ - nasm_free(params); - nasm_free(paramsize); - free_tlist(mstart); - free_tlist(eparams); - if (m->magic) - free_tlist(expansion); - expanded = true; - continue; /* main token loop */ - } - } - } - - if (tline->type == TOK_SMAC_END) { - /* On error path it might already be dropped */ - if (tline->a.mac) - tline->a.mac->in_progress = false; - tline = delete_Token(tline); - } else { - t = *tail = tline; - tline = tline->next; - t->a.mac = NULL; - t->next = NULL; - tail = &t->next; - } + /* + * An expansion happened if and only if the first token of the + * expansion was the first token of the original simply moved over. + */ + expanded |= (expand_one_smacro(&tail) != t); } /* @@ -4967,62 +4999,74 @@ static void pp_verror(errflags severity, const char *fmt, va_list arg) } } -static Token *stdmac_file(const SMacro *s, Token **params, int *paramsize) +static Token *stdmac_file(const SMacro *s, Token **params, + unsigned int nparams, bool *free_expansion) { (void)s; (void)params; - (void)paramsize; + (void)nparams; + *free_expansion = true; return make_tok_qstr(src_get_fname()); } -static Token *stdmac_line(const SMacro *s, Token **params, int *paramsize) +static Token *stdmac_line(const SMacro *s, Token **params, + unsigned int nparams, bool *free_expansion) { (void)s; (void)params; - (void)paramsize; + (void)nparams; + *free_expansion = true; return make_tok_num(src_get_linnum()); } -static Token *stdmac_bits(const SMacro *s, Token **params, int *paramsize) +static Token *stdmac_bits(const SMacro *s, Token **params, + unsigned int nparams, bool *free_expansion) { (void)s; (void)params; - (void)paramsize; + (void)nparams; + *free_expansion = true; return make_tok_num(globalbits); } -static Token *stdmac_ptr(const SMacro *s, Token **params, int *paramsize) +static Token *stdmac_ptr(const SMacro *s, Token **params, + unsigned int nparams, bool *free_expansion) { - const char *name; + const Token *t; + static const Token ptr_word = { NULL, "word", 4, TOKEN_ID }; + static const Token ptr_dword = { NULL, "dword", 5, TOKEN_ID }; + static const Token ptr_qword = { NULL, "qword", 5, TOKEN_ID }; (void)s; (void)params; - (void)paramsize; + (void)nparams; switch (globalbits) { case 16: - name = "word"; + t = &ptr_word; break; case 32: - name = "dword"; + t = &ptr_dword; break; case 64: - name = "qword"; + t = &ptr_qword; break; default: panic(); } - return new_Token(NULL, TOK_ID, name, 0); + + *free_expansion = false; + return (Token *)t; } /* Add magic standard macros */ struct magic_macros { const char *name; int nparams; - Token *(*func)(const SMacro *s, Token **params, int *paramsize); + MagicSMacro func; }; static const struct magic_macros magic_macros[] = { @@ -5040,8 +5084,7 @@ static void pp_add_magic_stdmac(void) for (m = magic_macros; m->name; m++) { s = define_smacro(NULL, m->name, true, m->nparams, NULL); - s->magic = true; - s->e.magic = m->func; + s->magic = m->func; } } @@ -5503,7 +5546,7 @@ static Token *make_tok_num(int64_t val) static Token *make_tok_qstr(const char *str) { Token *t = new_Token(NULL, TOK_STRING, NULL, 0); - t->text = nasm_quote_cstr(str); + t->text = nasm_quote_cstr(str, &t->len); return t; } diff --git a/asm/quote.c b/asm/quote.c index 962ae9fd..efd5cd6c 100644 --- a/asm/quote.c +++ b/asm/quote.c @@ -41,13 +41,19 @@ #include "nasmlib.h" #include "quote.h" -char *nasm_quote(const char *str, size_t len) +/* + * Create a NASM quoted string in newly allocated memory. Update the + * *lenp parameter with the output length (sans final NUL). + */ + +char *nasm_quote(const char *str, size_t *lenp) { const char *p, *ep; char c, c1, *q, *nstr; unsigned char uc; bool sq_ok, dq_ok; size_t qlen; + size_t len = *lenp; sq_ok = dq_ok = true; ep = str+len; @@ -105,7 +111,7 @@ char *nasm_quote(const char *str, size_t len) /* Use '...' or "..." */ nstr = nasm_malloc(len+3); nstr[0] = nstr[len+1] = sq_ok ? '\'' : '\"'; - nstr[len+2] = '\0'; + q = &nstr[len+2]; if (len > 0) memcpy(nstr+1, str, len); } else { @@ -174,9 +180,10 @@ char *nasm_quote(const char *str, size_t len) } } *q++ = '`'; - *q++ = '\0'; - nasm_assert((size_t)(q-nstr) == qlen+3); + nasm_assert((size_t)(q-nstr) == qlen+2); } + *q = '\0'; + *lenp = q - nstr; return nstr; } @@ -216,11 +223,16 @@ static char *emit_utf8(char *q, int32_t v) } /* - * Quote a C string + * Same as nasm_quote, but take the length of a C string; + * the lenp argument is optional. */ -char *nasm_quote_cstr(const char *str) +char *nasm_quote_cstr(const char *str, size_t *lenp) { - return nasm_quote(str, strlen(str)); + size_t len = strlen(str); + char *qstr = nasm_quote(str, &len); + if (lenp) + *lenp = len; + return qstr; } /* diff --git a/asm/quote.h b/asm/quote.h index ed934f25..0267cdd4 100644 --- a/asm/quote.h +++ b/asm/quote.h @@ -36,8 +36,8 @@ #include "compiler.h" -char *nasm_quote(const char *str, size_t len); -char *nasm_quote_cstr(const char *str); +char *nasm_quote(const char *str, size_t *len); +char *nasm_quote_cstr(const char *str, size_t *len); size_t nasm_unquote(char *str, char **endptr); char *nasm_skip_string(char *str); diff --git a/include/nasmlib.h b/include/nasmlib.h index c41a5bad..a5994528 100644 --- a/include/nasmlib.h +++ b/include/nasmlib.h @@ -41,6 +41,19 @@ #include "compiler.h" #include "bytesex.h" +/* + * Useful construct for private values + */ +union intorptr { + int64_t i; + uint64_t u; + size_t s; + void *p; + const void *cp; + uintptr_t up; +}; +typedef union intorptr intorptr; + /* * Wrappers around malloc, realloc, free and a few more. nasm_malloc * will fatal-error and die rather than return NULL; nasm_realloc will diff --git a/nasmlib/raa.c b/nasmlib/raa.c index feb86970..038f97ac 100644 --- a/nasmlib/raa.c +++ b/nasmlib/raa.c @@ -50,11 +50,6 @@ typedef union RAA_UNION RAA_UNION; typedef struct RAA_LEAF RAA_LEAF; typedef struct RAA_BRANCH RAA_BRANCH; -union intorptr { - int64_t i; - void *p; -}; - struct RAA { /* Last position in this RAA */ raaindex endposn;