emit1.c revision 1.17 1 /* $NetBSD: emit1.c,v 1.17 2008/04/25 22:18:34 christos Exp $ */
2
3 /*
4 * Copyright (c) 1996 Christopher G. Demetriou. All Rights Reserved.
5 * Copyright (c) 1994, 1995 Jochen Pohl
6 * All Rights Reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 * must display the following acknowledgement:
18 * This product includes software developed by Jochen Pohl for
19 * The NetBSD Project.
20 * 4. The name of the author may not be used to endorse or promote products
21 * derived from this software without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
24 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
25 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
26 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
27 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
28 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
32 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 */
34
35 #if HAVE_NBTOOL_CONFIG_H
36 #include "nbtool_config.h"
37 #endif
38
39 #include <sys/cdefs.h>
40 #if defined(__RCSID) && !defined(lint)
41 __RCSID("$NetBSD: emit1.c,v 1.17 2008/04/25 22:18:34 christos Exp $");
42 #endif
43
44 #include <ctype.h>
45
46 #include "lint1.h"
47
48 static void outtt(sym_t *, sym_t *);
49 static void outfstrg(strg_t *);
50
51 /*
52 * Write type into the output buffer.
53 * The type is written as a sequence of substrings, each of which describes a
54 * node of type type_t
55 * a node is coded as follows:
56 * _Bool B
57 * _Complex double u X
58 * _Complex float s X
59 * char C
60 * signed char s C
61 * unsigned char u C
62 * short S
63 * unsigned short u S
64 * int I
65 * unsigned int u I
66 * long L
67 * unsigned long u L
68 * long long Q
69 * unsigned long long u Q
70 * float s D
71 * double D
72 * long double l D
73 * void V
74 * * P
75 * [n] A n
76 * () F
77 * (void) F 0
78 * (n arguments) F n arg1 arg2 ... argn
79 * (n arguments, ...) F n arg1 arg2 ... argn-1 E
80 * (a, b, c, ...) f n arg1 arg2 ...
81 * enum tag e T tag_or_typename
82 * struct tag s T tag_or_typename
83 * union tag u T tag_or_typename
84 *
85 * tag_or_typename 0 no tag or type name
86 * 1 n tag Tag
87 * 2 n typename only type name
88 *
89 * spaces are only for better readability
90 * additionaly it is possible to prepend the characters 'c' (for const)
91 * and 'v' (for volatile)
92 */
93 void
94 outtype(type_t *tp)
95 {
96 int t, s, na;
97 sym_t *arg;
98 tspec_t ts;
99
100 while (tp != NULL) {
101 if ((ts = tp->t_tspec) == INT && tp->t_isenum)
102 ts = ENUM;
103 switch (ts) {
104 case BOOL: t = 'B'; s = '\0'; break;
105 case CHAR: t = 'C'; s = '\0'; break;
106 case SCHAR: t = 'C'; s = 's'; break;
107 case UCHAR: t = 'C'; s = 'u'; break;
108 case SHORT: t = 'S'; s = '\0'; break;
109 case USHORT: t = 'S'; s = 'u'; break;
110 case INT: t = 'I'; s = '\0'; break;
111 case UINT: t = 'I'; s = 'u'; break;
112 case LONG: t = 'L'; s = '\0'; break;
113 case ULONG: t = 'L'; s = 'u'; break;
114 case QUAD: t = 'Q'; s = '\0'; break;
115 case UQUAD: t = 'Q'; s = 'u'; break;
116 case FLOAT: t = 'D'; s = 's'; break;
117 case DOUBLE: t = 'D'; s = '\0'; break;
118 case LDOUBLE: t = 'D'; s = 'l'; break;
119 case VOID: t = 'V'; s = '\0'; break;
120 case PTR: t = 'P'; s = '\0'; break;
121 case ARRAY: t = 'A'; s = '\0'; break;
122 case FUNC: t = 'F'; s = '\0'; break;
123 case ENUM: t = 'T'; s = 'e'; break;
124 case STRUCT: t = 'T'; s = 's'; break;
125 case UNION: t = 'T'; s = 'u'; break;
126 case DCOMPLEX: t = 'X'; s = 'u'; break;
127 case FCOMPLEX: t = 'X'; s = 's'; break;
128 default:
129 LERROR("outtyp()");
130 }
131 if (tp->t_const)
132 outchar('c');
133 if (tp->t_volatile)
134 outchar('v');
135 if (s != '\0')
136 outchar(s);
137 outchar(t);
138 if (ts == ARRAY) {
139 outint(tp->t_dim);
140 } else if (ts == ENUM) {
141 outtt(tp->t_enum->etag, tp->t_enum->etdef);
142 } else if (ts == STRUCT || ts == UNION) {
143 outtt(tp->t_str->stag, tp->t_str->stdef);
144 } else if (ts == FUNC && tp->t_proto) {
145 na = 0;
146 for (arg = tp->t_args; arg != NULL; arg = arg->s_nxt)
147 na++;
148 if (tp->t_vararg)
149 na++;
150 outint(na);
151 for (arg = tp->t_args; arg != NULL; arg = arg->s_nxt)
152 outtype(arg->s_type);
153 if (tp->t_vararg)
154 outchar('E');
155 }
156 tp = tp->t_subt;
157 }
158 }
159
160 /*
161 * type to string
162 * used for debugging output
163 *
164 * it uses its own output buffer for conversion
165 */
166 const char *
167 ttos(type_t *tp)
168 {
169 static ob_t tob;
170 ob_t tmp;
171
172 if (tob.o_buf == NULL) {
173 tob.o_len = 64;
174 tob.o_buf = tob.o_nxt = xmalloc(tob.o_len);
175 tob.o_end = tob.o_buf + tob.o_len;
176 }
177
178 tmp = ob;
179 ob = tob;
180 ob.o_nxt = ob.o_buf;
181 outtype(tp);
182 outchar('\0');
183 tob = ob;
184 ob = tmp;
185
186 return (tob.o_buf);
187 }
188
189 /*
190 * write the name of a tag or typename
191 *
192 * if the tag is named, the name of the
193 * tag is written, otherwise, if a typename exists which
194 * refers to this tag, this typename is written
195 */
196 static void
197 outtt(sym_t *tag, sym_t *tdef)
198 {
199
200 /*
201 * 0 is no longer used.
202 */
203 if (tag->s_name != unnamed) {
204 outint(1);
205 outname(tag->s_name);
206 } else if (tdef != NULL) {
207 outint(2);
208 outname(tdef->s_name);
209 } else {
210 outint(3);
211 outint(tag->s_dpos.p_line);
212 outchar('.');
213 outint(getfnid(tag->s_dpos.p_file));
214 outchar('.');
215 outint(tag->s_dpos.p_uniq);
216 }
217 }
218
219 /*
220 * write information about an global declared/defined symbol
221 * with storage class extern
222 *
223 * informations about function definitions are written in outfdef(),
224 * not here
225 */
226 void
227 outsym(sym_t *sym, scl_t sc, def_t def)
228 {
229
230 /*
231 * Static function declarations must also be written to the output
232 * file. Compatibility of function declarations (for both static
233 * and extern functions) must be checked in lint2. Lint1 can't do
234 * this, especially not, if functions are declared at block level
235 * before their first declaration at level 0.
236 */
237 if (sc != EXTERN && !(sc == STATIC && sym->s_type->t_tspec == FUNC))
238 return;
239
240 /* reset buffer */
241 outclr();
242
243 /*
244 * line number of .c source, 'd' for declaration, Id of current
245 * source (.c or .h), and line in current source.
246 */
247 outint(csrc_pos.p_line);
248 outchar('d');
249 outint(getfnid(sym->s_dpos.p_file));
250 outchar('.');
251 outint(sym->s_dpos.p_line);
252
253 /* flags */
254
255 switch (def) {
256 case DEF:
257 /* defined */
258 outchar('d');
259 break;
260 case TDEF:
261 /* tentative defined */
262 outchar('t');
263 break;
264 case DECL:
265 /* declared */
266 outchar('e');
267 break;
268 default:
269 LERROR("outsym()");
270 }
271 if (llibflg && def != DECL) {
272 /*
273 * mark it as used so we get no warnings from lint2 about
274 * unused symbols in libraries.
275 */
276 outchar('u');
277 }
278
279 if (sc == STATIC)
280 outchar('s');
281
282 /* name of the symbol */
283 outname(sym->s_name);
284
285 /* renamed name of symbol, if necessary */
286 if (sym->s_rename) {
287 outchar('r');
288 outname(sym->s_rename);
289 }
290
291 /* type of the symbol */
292 outtype(sym->s_type);
293 }
294
295 /*
296 * write information about function definition
297 *
298 * this is also done for static functions so we are able to check if
299 * they are called with proper argument types
300 */
301 void
302 outfdef(sym_t *fsym, pos_t *posp, int rval, int osdef, sym_t *args)
303 {
304 int narg;
305 sym_t *arg;
306
307 /* reset the buffer */
308 outclr();
309
310 /*
311 * line number of .c source, 'd' for declaration, Id of current
312 * source (.c or .h), and line in current source
313 *
314 * we are already at the end of the function. If we are in the
315 * .c source, posp->p_line is correct, otherwise csrc_pos.p_line
316 * (for functions defined in header files).
317 */
318 if (posp->p_file == csrc_pos.p_file) {
319 outint(posp->p_line);
320 } else {
321 outint(csrc_pos.p_line);
322 }
323 outchar('d');
324 outint(getfnid(posp->p_file));
325 outchar('.');
326 outint(posp->p_line);
327
328 /* flags */
329
330 /* both SCANFLIKE and PRINTFLIKE imply VARARGS */
331 if (prflstrg != -1) {
332 nvararg = prflstrg;
333 } else if (scflstrg != -1) {
334 nvararg = scflstrg;
335 }
336
337 if (nvararg != -1) {
338 outchar('v');
339 outint(nvararg);
340 }
341 if (scflstrg != -1) {
342 outchar('S');
343 outint(scflstrg);
344 }
345 if (prflstrg != -1) {
346 outchar('P');
347 outint(prflstrg);
348 }
349 nvararg = prflstrg = scflstrg = -1;
350
351 outchar('d');
352
353 if (rval)
354 /* has return value */
355 outchar('r');
356
357 if (llibflg)
358 /*
359 * mark it as used so lint2 does not complain about
360 * unused symbols in libraries
361 */
362 outchar('u');
363
364 if (osdef)
365 /* old style function definition */
366 outchar('o');
367
368 if (fsym->s_scl == STATIC)
369 outchar('s');
370
371 /* name of function */
372 outname(fsym->s_name);
373
374 /* renamed name of function, if necessary */
375 if (fsym->s_rename) {
376 outchar('r');
377 outname(fsym->s_rename);
378 }
379
380 /* argument types and return value */
381 if (osdef) {
382 narg = 0;
383 for (arg = args; arg != NULL; arg = arg->s_nxt)
384 narg++;
385 outchar('f');
386 outint(narg);
387 for (arg = args; arg != NULL; arg = arg->s_nxt)
388 outtype(arg->s_type);
389 outtype(fsym->s_type->t_subt);
390 } else {
391 outtype(fsym->s_type);
392 }
393 }
394
395 /*
396 * write out all information necessary for lint2 to check function
397 * calls
398 *
399 * rvused is set if the return value is used (asigned to a variable)
400 * rvdisc is set if the return value is not used and not ignored
401 * (casted to void)
402 */
403 void
404 outcall(tnode_t *tn, int rvused, int rvdisc)
405 {
406 tnode_t *args, *arg;
407 int narg, n, i;
408 int64_t q;
409 tspec_t t;
410
411 /* reset buffer */
412 outclr();
413
414 /*
415 * line number of .c source, 'c' for function call, Id of current
416 * source (.c or .h), and line in current source
417 */
418 outint(csrc_pos.p_line);
419 outchar('c');
420 outint(getfnid(curr_pos.p_file));
421 outchar('.');
422 outint(curr_pos.p_line);
423
424 /*
425 * flags; 'u' and 'i' must be last to make sure a letter
426 * is between the numeric argument of a flag and the name of
427 * the function
428 */
429 narg = 0;
430 args = tn->tn_right;
431 for (arg = args; arg != NULL; arg = arg->tn_right)
432 narg++;
433 /* informations about arguments */
434 for (n = 1; n <= narg; n++) {
435 /* the last argument is the top one in the tree */
436 for (i = narg, arg = args; i > n; i--, arg = arg->tn_right)
437 continue;
438 arg = arg->tn_left;
439 if (arg->tn_op == CON) {
440 if (isityp(t = arg->tn_type->t_tspec)) {
441 /*
442 * XXX it would probably be better to
443 * explicitly test the sign
444 */
445 if ((q = arg->tn_val->v_quad) == 0) {
446 /* zero constant */
447 outchar('z');
448 } else if (msb(q, t, 0) == 0) {
449 /* positive if casted to signed */
450 outchar('p');
451 } else {
452 /* negative if casted to signed */
453 outchar('n');
454 }
455 outint(n);
456 }
457 } else if (arg->tn_op == AMPER &&
458 arg->tn_left->tn_op == STRING &&
459 arg->tn_left->tn_strg->st_tspec == CHAR) {
460 /* constant string, write all format specifiers */
461 outchar('s');
462 outint(n);
463 outfstrg(arg->tn_left->tn_strg);
464 }
465
466 }
467 /* return value discarded/used/ignored */
468 outchar(rvdisc ? 'd' : (rvused ? 'u' : 'i'));
469
470 /* name of the called function */
471 outname(tn->tn_left->tn_left->tn_sym->s_name);
472
473 /* types of arguments */
474 outchar('f');
475 outint(narg);
476 for (n = 1; n <= narg; n++) {
477 /* the last argument is the top one in the tree */
478 for (i = narg, arg = args; i > n; i--, arg = arg->tn_right)
479 continue;
480 outtype(arg->tn_left->tn_type);
481 }
482 /* expected type of return value */
483 outtype(tn->tn_type);
484 }
485
486 /*
487 * extracts potential format specifiers for printf() and scanf() and
488 * writes them, enclosed in "" and qouted if necessary, to the output buffer
489 */
490 static void
491 outfstrg(strg_t *strg)
492 {
493 int c, oc, first;
494 u_char *cp;
495
496 if (strg->st_tspec != CHAR)
497 LERROR("outfstrg()");
498
499 cp = strg->st_cp;
500
501 outchar('"');
502
503 c = *cp++;
504
505 while (c != '\0') {
506
507 if (c != '%') {
508 c = *cp++;
509 continue;
510 }
511
512 outqchar('%');
513 c = *cp++;
514
515 /* flags for printf and scanf and *-fieldwidth for printf */
516 while (c != '\0' && (c == '-' || c == '+' || c == ' ' ||
517 c == '#' || c == '0' || c == '*')) {
518 outqchar(c);
519 c = *cp++;
520 }
521
522 /* numeric field width */
523 while (c != '\0' && isdigit(c)) {
524 outqchar(c);
525 c = *cp++;
526 }
527
528 /* precision for printf */
529 if (c == '.') {
530 outqchar(c);
531 if ((c = *cp++) == '*') {
532 outqchar(c);
533 c = *cp++;
534 } else {
535 while (c != '\0' && isdigit(c)) {
536 outqchar(c);
537 c = *cp++;
538 }
539 }
540 }
541
542 /* h, l, L and q flags fpr printf and scanf */
543 if (c == 'h' || c == 'l' || c == 'L' || c == 'q') {
544 outqchar(c);
545 c = *cp++;
546 }
547
548 /*
549 * The last character. It is always written so we can detect
550 * invalid format specifiers.
551 */
552 if (c != '\0') {
553 outqchar(c);
554 oc = c;
555 c = *cp++;
556 /*
557 * handle [ for scanf. [-] means that a minus sign
558 * was found at an undefined position.
559 */
560 if (oc == '[') {
561 if (c == '^')
562 c = *cp++;
563 if (c == ']')
564 c = *cp++;
565 first = 1;
566 while (c != '\0' && c != ']') {
567 if (c == '-') {
568 if (!first && *cp != ']')
569 outqchar(c);
570 }
571 first = 0;
572 c = *cp++;
573 }
574 if (c == ']') {
575 outqchar(c);
576 c = *cp++;
577 }
578 }
579 }
580
581 }
582
583 outchar('"');
584 }
585
586 /*
587 * writes a record if sym was used
588 */
589 void
590 outusg(sym_t *sym)
591 {
592 /* reset buffer */
593 outclr();
594
595 /*
596 * line number of .c source, 'u' for used, Id of current
597 * source (.c or .h), and line in current source
598 */
599 outint(csrc_pos.p_line);
600 outchar('u');
601 outint(getfnid(curr_pos.p_file));
602 outchar('.');
603 outint(curr_pos.p_line);
604
605 /* necessary to delimit both numbers */
606 outchar('x');
607
608 /* Den Namen des Symbols ausgeben */
609 outname(sym->s_name);
610 }
611