601 lines
13 KiB
C
601 lines
13 KiB
C
/* $NetBSD: emit1.c,v 1.13 2002/09/13 14:59:24 christos Exp $ */
|
|
|
|
/*
|
|
* Copyright (c) 1996 Christopher G. Demetriou. All Rights Reserved.
|
|
* Copyright (c) 1994, 1995 Jochen Pohl
|
|
* All Rights Reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. All advertising materials mentioning features or use of this software
|
|
* must display the following acknowledgement:
|
|
* This product includes software developed by Jochen Pohl for
|
|
* The NetBSD Project.
|
|
* 4. The name of the author may not be used to endorse or promote products
|
|
* derived from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
#if defined(__RCSID) && !defined(lint)
|
|
__RCSID("$NetBSD: emit1.c,v 1.13 2002/09/13 14:59:24 christos Exp $");
|
|
#endif
|
|
|
|
#include <ctype.h>
|
|
|
|
#include "lint1.h"
|
|
|
|
static void outtt(sym_t *, sym_t *);
|
|
static void outfstrg(strg_t *);
|
|
|
|
/*
|
|
* Write type into the output buffer.
|
|
* The type is written as a sequence of substrings, each of which describes a
|
|
* node of type type_t
|
|
* a node is coded as follows:
|
|
* char C
|
|
* signed char s C
|
|
* unsigned char u C
|
|
* short S
|
|
* unsigned short u S
|
|
* int I
|
|
* unsigned int u I
|
|
* long L
|
|
* unsigned long u L
|
|
* long long Q
|
|
* unsigned long long u Q
|
|
* float s D
|
|
* double D
|
|
* long double l D
|
|
* void V
|
|
* * P
|
|
* [n] A n
|
|
* () F
|
|
* (void) F 0
|
|
* (n arguments) F n arg1 arg2 ... argn
|
|
* (n arguments, ...) F n arg1 arg2 ... argn-1 E
|
|
* (a, b, c, ...) f n arg1 arg2 ...
|
|
* enum tag e T tag_or_typename
|
|
* struct tag s T tag_or_typename
|
|
* union tag u T tag_or_typename
|
|
*
|
|
* tag_or_typename 0 no tag or type name
|
|
* 1 n tag Tag
|
|
* 2 n typename only type name
|
|
*
|
|
* spaces are only for better readability
|
|
* additionaly it is possible to prepend the characters 'c' (for const)
|
|
* and 'v' (for volatile)
|
|
*/
|
|
void
|
|
outtype(type_t *tp)
|
|
{
|
|
int t, s, na;
|
|
sym_t *arg;
|
|
tspec_t ts;
|
|
|
|
while (tp != NULL) {
|
|
if ((ts = tp->t_tspec) == INT && tp->t_isenum)
|
|
ts = ENUM;
|
|
switch (ts) {
|
|
case CHAR: t = 'C'; s = '\0'; break;
|
|
case SCHAR: t = 'C'; s = 's'; break;
|
|
case UCHAR: t = 'C'; s = 'u'; break;
|
|
case SHORT: t = 'S'; s = '\0'; break;
|
|
case USHORT: t = 'S'; s = 'u'; break;
|
|
case INT: t = 'I'; s = '\0'; break;
|
|
case UINT: t = 'I'; s = 'u'; break;
|
|
case LONG: t = 'L'; s = '\0'; break;
|
|
case ULONG: t = 'L'; s = 'u'; break;
|
|
case QUAD: t = 'Q'; s = '\0'; break;
|
|
case UQUAD: t = 'Q'; s = 'u'; break;
|
|
case FLOAT: t = 'D'; s = 's'; break;
|
|
case DOUBLE: t = 'D'; s = '\0'; break;
|
|
case LDOUBLE: t = 'D'; s = 'l'; break;
|
|
case VOID: t = 'V'; s = '\0'; break;
|
|
case PTR: t = 'P'; s = '\0'; break;
|
|
case ARRAY: t = 'A'; s = '\0'; break;
|
|
case FUNC: t = 'F'; s = '\0'; break;
|
|
case ENUM: t = 'T'; s = 'e'; break;
|
|
case STRUCT: t = 'T'; s = 's'; break;
|
|
case UNION: t = 'T'; s = 'u'; break;
|
|
default:
|
|
LERROR("outtyp()");
|
|
}
|
|
if (tp->t_const)
|
|
outchar('c');
|
|
if (tp->t_volatile)
|
|
outchar('v');
|
|
if (s != '\0')
|
|
outchar(s);
|
|
outchar(t);
|
|
if (ts == ARRAY) {
|
|
outint(tp->t_dim);
|
|
} else if (ts == ENUM) {
|
|
outtt(tp->t_enum->etag, tp->t_enum->etdef);
|
|
} else if (ts == STRUCT || ts == UNION) {
|
|
outtt(tp->t_str->stag, tp->t_str->stdef);
|
|
} else if (ts == FUNC && tp->t_proto) {
|
|
na = 0;
|
|
for (arg = tp->t_args; arg != NULL; arg = arg->s_nxt)
|
|
na++;
|
|
if (tp->t_vararg)
|
|
na++;
|
|
outint(na);
|
|
for (arg = tp->t_args; arg != NULL; arg = arg->s_nxt)
|
|
outtype(arg->s_type);
|
|
if (tp->t_vararg)
|
|
outchar('E');
|
|
}
|
|
tp = tp->t_subt;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* type to string
|
|
* used for debugging output
|
|
*
|
|
* it uses its own output buffer for conversion
|
|
*/
|
|
const char *
|
|
ttos(type_t *tp)
|
|
{
|
|
static ob_t tob;
|
|
ob_t tmp;
|
|
|
|
if (tob.o_buf == NULL) {
|
|
tob.o_len = 64;
|
|
tob.o_buf = tob.o_nxt = xmalloc(tob.o_len);
|
|
tob.o_end = tob.o_buf + tob.o_len;
|
|
}
|
|
|
|
tmp = ob;
|
|
ob = tob;
|
|
ob.o_nxt = ob.o_buf;
|
|
outtype(tp);
|
|
outchar('\0');
|
|
tob = ob;
|
|
ob = tmp;
|
|
|
|
return (tob.o_buf);
|
|
}
|
|
|
|
/*
|
|
* write the name of a tag or typename
|
|
*
|
|
* if the tag is named, the name of the
|
|
* tag is written, otherwise, if a typename exists which
|
|
* refers to this tag, this typename is written
|
|
*/
|
|
static void
|
|
outtt(sym_t *tag, sym_t *tdef)
|
|
{
|
|
|
|
/*
|
|
* 0 is no longer used.
|
|
*/
|
|
if (tag->s_name != unnamed) {
|
|
outint(1);
|
|
outname(tag->s_name);
|
|
} else if (tdef != NULL) {
|
|
outint(2);
|
|
outname(tdef->s_name);
|
|
} else {
|
|
outint(3);
|
|
outint(tag->s_dpos.p_line);
|
|
outchar('.');
|
|
outint(getfnid(tag->s_dpos.p_file));
|
|
outchar('.');
|
|
outint(tag->s_dpos.p_uniq);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* write information about an global declared/defined symbol
|
|
* with storage class extern
|
|
*
|
|
* informations about function definitions are written in outfdef(),
|
|
* not here
|
|
*/
|
|
void
|
|
outsym(sym_t *sym, scl_t sc, def_t def)
|
|
{
|
|
|
|
/*
|
|
* Static function declarations must also be written to the output
|
|
* file. Compatibility of function declarations (for both static
|
|
* and extern functions) must be checked in lint2. Lint1 can't do
|
|
* this, especially not, if functions are declared at block level
|
|
* before their first declaration at level 0.
|
|
*/
|
|
if (sc != EXTERN && !(sc == STATIC && sym->s_type->t_tspec == FUNC))
|
|
return;
|
|
|
|
/* reset buffer */
|
|
outclr();
|
|
|
|
/*
|
|
* line number of .c source, 'd' for declaration, Id of current
|
|
* source (.c or .h), and line in current source.
|
|
*/
|
|
outint(csrc_pos.p_line);
|
|
outchar('d');
|
|
outint(getfnid(sym->s_dpos.p_file));
|
|
outchar('.');
|
|
outint(sym->s_dpos.p_line);
|
|
|
|
/* flags */
|
|
|
|
switch (def) {
|
|
case DEF:
|
|
/* defined */
|
|
outchar('d');
|
|
break;
|
|
case TDEF:
|
|
/* tentative defined */
|
|
outchar('t');
|
|
break;
|
|
case DECL:
|
|
/* declared */
|
|
outchar('e');
|
|
break;
|
|
default:
|
|
LERROR("outsym()");
|
|
}
|
|
if (llibflg && def != DECL) {
|
|
/*
|
|
* mark it as used so we get no warnings from lint2 about
|
|
* unused symbols in libraries.
|
|
*/
|
|
outchar('u');
|
|
}
|
|
|
|
if (sc == STATIC)
|
|
outchar('s');
|
|
|
|
/* name of the symbol */
|
|
outname(sym->s_name);
|
|
|
|
/* renamed name of symbol, if necessary */
|
|
if (sym->s_rename) {
|
|
outchar('r');
|
|
outname(sym->s_rename);
|
|
}
|
|
|
|
/* type of the symbol */
|
|
outtype(sym->s_type);
|
|
}
|
|
|
|
/*
|
|
* write information about function definition
|
|
*
|
|
* this is also done for static functions so we are able to check if
|
|
* they are called with proper argument types
|
|
*/
|
|
void
|
|
outfdef(sym_t *fsym, pos_t *posp, int rval, int osdef, sym_t *args)
|
|
{
|
|
int narg;
|
|
sym_t *arg;
|
|
|
|
/* reset the buffer */
|
|
outclr();
|
|
|
|
/*
|
|
* line number of .c source, 'd' for declaration, Id of current
|
|
* source (.c or .h), and line in current source
|
|
*
|
|
* we are already at the end of the function. If we are in the
|
|
* .c source, posp->p_line is correct, otherwise csrc_pos.p_line
|
|
* (for functions defined in header files).
|
|
*/
|
|
if (posp->p_file == csrc_pos.p_file) {
|
|
outint(posp->p_line);
|
|
} else {
|
|
outint(csrc_pos.p_line);
|
|
}
|
|
outchar('d');
|
|
outint(getfnid(posp->p_file));
|
|
outchar('.');
|
|
outint(posp->p_line);
|
|
|
|
/* flags */
|
|
|
|
/* both SCANFLIKE and PRINTFLIKE imply VARARGS */
|
|
if (prflstrg != -1) {
|
|
nvararg = prflstrg;
|
|
} else if (scflstrg != -1) {
|
|
nvararg = scflstrg;
|
|
}
|
|
|
|
if (nvararg != -1) {
|
|
outchar('v');
|
|
outint(nvararg);
|
|
}
|
|
if (scflstrg != -1) {
|
|
outchar('S');
|
|
outint(scflstrg);
|
|
}
|
|
if (prflstrg != -1) {
|
|
outchar('P');
|
|
outint(prflstrg);
|
|
}
|
|
nvararg = prflstrg = scflstrg = -1;
|
|
|
|
outchar('d');
|
|
|
|
if (rval)
|
|
/* has return value */
|
|
outchar('r');
|
|
|
|
if (llibflg)
|
|
/*
|
|
* mark it as used so lint2 does not complain about
|
|
* unused symbols in libraries
|
|
*/
|
|
outchar('u');
|
|
|
|
if (osdef)
|
|
/* old style function definition */
|
|
outchar('o');
|
|
|
|
if (fsym->s_scl == STATIC)
|
|
outchar('s');
|
|
|
|
/* name of function */
|
|
outname(fsym->s_name);
|
|
|
|
/* renamed name of function, if necessary */
|
|
if (fsym->s_rename) {
|
|
outchar('r');
|
|
outname(fsym->s_rename);
|
|
}
|
|
|
|
/* argument types and return value */
|
|
if (osdef) {
|
|
narg = 0;
|
|
for (arg = args; arg != NULL; arg = arg->s_nxt)
|
|
narg++;
|
|
outchar('f');
|
|
outint(narg);
|
|
for (arg = args; arg != NULL; arg = arg->s_nxt)
|
|
outtype(arg->s_type);
|
|
outtype(fsym->s_type->t_subt);
|
|
} else {
|
|
outtype(fsym->s_type);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* write out all information necessary for lint2 to check function
|
|
* calls
|
|
*
|
|
* rvused is set if the return value is used (asigned to a variable)
|
|
* rvdisc is set if the return value is not used and not ignored
|
|
* (casted to void)
|
|
*/
|
|
void
|
|
outcall(tnode_t *tn, int rvused, int rvdisc)
|
|
{
|
|
tnode_t *args, *arg;
|
|
int narg, n, i;
|
|
int64_t q;
|
|
tspec_t t;
|
|
|
|
/* reset buffer */
|
|
outclr();
|
|
|
|
/*
|
|
* line number of .c source, 'c' for function call, Id of current
|
|
* source (.c or .h), and line in current source
|
|
*/
|
|
outint(csrc_pos.p_line);
|
|
outchar('c');
|
|
outint(getfnid(curr_pos.p_file));
|
|
outchar('.');
|
|
outint(curr_pos.p_line);
|
|
|
|
/*
|
|
* flags; 'u' and 'i' must be last to make sure a letter
|
|
* is between the numeric argument of a flag and the name of
|
|
* the function
|
|
*/
|
|
narg = 0;
|
|
args = tn->tn_right;
|
|
for (arg = args; arg != NULL; arg = arg->tn_right)
|
|
narg++;
|
|
/* informations about arguments */
|
|
for (n = 1; n <= narg; n++) {
|
|
/* the last argument is the top one in the tree */
|
|
for (i = narg, arg = args; i > n; i--, arg = arg->tn_right)
|
|
continue;
|
|
arg = arg->tn_left;
|
|
if (arg->tn_op == CON) {
|
|
if (isityp(t = arg->tn_type->t_tspec)) {
|
|
/*
|
|
* XXX it would probably be better to
|
|
* explizitly test the sign
|
|
*/
|
|
if ((q = arg->tn_val->v_quad) == 0) {
|
|
/* zero constant */
|
|
outchar('z');
|
|
} else if (msb(q, t, 0) == 0) {
|
|
/* positive if casted to signed */
|
|
outchar('p');
|
|
} else {
|
|
/* negative if casted to signed */
|
|
outchar('n');
|
|
}
|
|
outint(n);
|
|
}
|
|
} else if (arg->tn_op == AMPER &&
|
|
arg->tn_left->tn_op == STRING &&
|
|
arg->tn_left->tn_strg->st_tspec == CHAR) {
|
|
/* constant string, write all format specifiers */
|
|
outchar('s');
|
|
outint(n);
|
|
outfstrg(arg->tn_left->tn_strg);
|
|
}
|
|
|
|
}
|
|
/* return value discarded/used/ignored */
|
|
outchar(rvdisc ? 'd' : (rvused ? 'u' : 'i'));
|
|
|
|
/* name of the called function */
|
|
outname(tn->tn_left->tn_left->tn_sym->s_name);
|
|
|
|
/* types of arguments */
|
|
outchar('f');
|
|
outint(narg);
|
|
for (n = 1; n <= narg; n++) {
|
|
/* the last argument is the top one in the tree */
|
|
for (i = narg, arg = args; i > n; i--, arg = arg->tn_right)
|
|
continue;
|
|
outtype(arg->tn_left->tn_type);
|
|
}
|
|
/* expected type of return value */
|
|
outtype(tn->tn_type);
|
|
}
|
|
|
|
/*
|
|
* extracts potential format specifiers for printf() and scanf() and
|
|
* writes them, enclosed in "" and qouted if necessary, to the output buffer
|
|
*/
|
|
static void
|
|
outfstrg(strg_t *strg)
|
|
{
|
|
int c, oc, first;
|
|
u_char *cp;
|
|
|
|
if (strg->st_tspec != CHAR)
|
|
LERROR("outfstrg()");
|
|
|
|
cp = strg->st_cp;
|
|
|
|
outchar('"');
|
|
|
|
c = *cp++;
|
|
|
|
while (c != '\0') {
|
|
|
|
if (c != '%') {
|
|
c = *cp++;
|
|
continue;
|
|
}
|
|
|
|
outqchar('%');
|
|
c = *cp++;
|
|
|
|
/* flags for printf and scanf and *-fieldwidth for printf */
|
|
while (c != '\0' && (c == '-' || c == '+' || c == ' ' ||
|
|
c == '#' || c == '0' || c == '*')) {
|
|
outqchar(c);
|
|
c = *cp++;
|
|
}
|
|
|
|
/* numeric field width */
|
|
while (c != '\0' && isdigit(c)) {
|
|
outqchar(c);
|
|
c = *cp++;
|
|
}
|
|
|
|
/* precision for printf */
|
|
if (c == '.') {
|
|
outqchar(c);
|
|
if ((c = *cp++) == '*') {
|
|
outqchar(c);
|
|
c = *cp++;
|
|
} else {
|
|
while (c != '\0' && isdigit(c)) {
|
|
outqchar(c);
|
|
c = *cp++;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* h, l, L and q flags fpr printf and scanf */
|
|
if (c == 'h' || c == 'l' || c == 'L' || c == 'q') {
|
|
outqchar(c);
|
|
c = *cp++;
|
|
}
|
|
|
|
/*
|
|
* The last character. It is always written so we can detect
|
|
* invalid format specifiers.
|
|
*/
|
|
if (c != '\0') {
|
|
outqchar(c);
|
|
oc = c;
|
|
c = *cp++;
|
|
/*
|
|
* handle [ for scanf. [-] means that a minus sign
|
|
* was found at an undefined position.
|
|
*/
|
|
if (oc == '[') {
|
|
if (c == '^')
|
|
c = *cp++;
|
|
if (c == ']')
|
|
c = *cp++;
|
|
first = 1;
|
|
while (c != '\0' && c != ']') {
|
|
if (c == '-') {
|
|
if (!first && *cp != ']')
|
|
outqchar(c);
|
|
}
|
|
first = 0;
|
|
c = *cp++;
|
|
}
|
|
if (c == ']') {
|
|
outqchar(c);
|
|
c = *cp++;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
outchar('"');
|
|
}
|
|
|
|
/*
|
|
* writes a record if sym was used
|
|
*/
|
|
void
|
|
outusg(sym_t *sym)
|
|
{
|
|
/* reset buffer */
|
|
outclr();
|
|
|
|
/*
|
|
* line number of .c source, 'u' for used, Id of current
|
|
* source (.c or .h), and line in current source
|
|
*/
|
|
outint(csrc_pos.p_line);
|
|
outchar('u');
|
|
outint(getfnid(curr_pos.p_file));
|
|
outchar('.');
|
|
outint(curr_pos.p_line);
|
|
|
|
/* necessary to delimit both numbers */
|
|
outchar('x');
|
|
|
|
/* Den Namen des Symbols ausgeben */
|
|
outname(sym->s_name);
|
|
}
|