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