/[jscoverage]/trunk/instrument-js.c
ViewVC logotype

Contents of /trunk/instrument-js.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2 - (show annotations)
Wed Aug 1 13:51:53 2007 UTC (15 years, 10 months ago) by siliconforks
File MIME type: text/plain
File size: 23223 byte(s)
Initial import.

1 /*
2 instrument-js.c - JavaScript instrumentation routines
3 Copyright (C) 2007 siliconforks.com
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "instrument-js.h"
21
22 #include <assert.h>
23 #include <stdlib.h>
24 #include <string.h>
25
26 #include <jsapi.h>
27 #include <jsatom.h>
28 #include <jsfun.h>
29 #include <jsinterp.h>
30 #include <jsparse.h>
31 #include <jsregexp.h>
32 #include <jsscope.h>
33 #include <jsstr.h>
34
35 #include "util.h"
36
37 static JSRuntime * runtime = NULL;
38 static JSContext * context = NULL;
39 static JSObject * global = NULL;
40
41 /*
42 JSParseNode objects store line numbers starting from 1.
43 The lines array stores line numbers starting from 0.
44 */
45 static const char * file_id = NULL;
46 static char * lines = NULL;
47
48 void jscoverage_init(void) {
49 runtime = JS_NewRuntime(8L * 1024L * 1024L);
50 if (runtime == NULL) {
51 fatal("cannot create runtime");
52 }
53
54 context = JS_NewContext(runtime, 8192);
55 if (context == NULL) {
56 fatal("cannot create context");
57 }
58
59 global = JS_NewObject(context, NULL, NULL, NULL);
60 if (global == NULL) {
61 fatal("cannot create global object");
62 }
63
64 if (! JS_InitStandardClasses(context, global)) {
65 fatal("cannot initialize standard classes");
66 }
67 }
68
69 void jscoverage_cleanup(void) {
70 JS_DestroyContext(context);
71 JS_DestroyRuntime(runtime);
72 }
73
74 static void print_string(JSString * s, FILE * f) {
75 for (int i = 0; i < s->length; i++) {
76 char c = s->chars[i];
77 fputc(c, f);
78 }
79 }
80
81 static void print_string_atom(JSAtom * atom, FILE * f) {
82 assert(ATOM_IS_STRING(atom));
83 JSString * s = ATOM_TO_STRING(atom);
84 print_string(s, f);
85 }
86
87 static void print_string_jsval(jsval value, FILE * f) {
88 assert(JSVAL_IS_STRING(value));
89 JSString * s = JSVAL_TO_STRING(value);
90 print_string(s, f);
91 }
92
93 static void print_quoted_string_atom(JSAtom * atom, FILE * f) {
94 assert(ATOM_IS_STRING(atom));
95 JSString * s = ATOM_TO_STRING(atom);
96 JSString * quoted = js_QuoteString(context, s, '"');
97 print_string(quoted, f);
98 }
99
100 static const char * get_op(uint8 op) {
101 switch(op) {
102 case JSOP_BITOR:
103 return "|";
104 case JSOP_BITXOR:
105 return "^";
106 case JSOP_BITAND:
107 return "&";
108 case JSOP_EQ:
109 return "==";
110 case JSOP_NE:
111 return "!=";
112 case JSOP_NEW_EQ:
113 return "===";
114 case JSOP_NEW_NE:
115 return "!==";
116 case JSOP_LT:
117 return "<";
118 case JSOP_LE:
119 return "<=";
120 case JSOP_GT:
121 return ">";
122 case JSOP_GE:
123 return ">=";
124 case JSOP_LSH:
125 return "<<";
126 case JSOP_RSH:
127 return ">>";
128 case JSOP_URSH:
129 return ">>>";
130 case JSOP_ADD:
131 return "+";
132 case JSOP_SUB:
133 return "-";
134 case JSOP_MUL:
135 return "*";
136 case JSOP_DIV:
137 return "/";
138 case JSOP_MOD:
139 return "%";
140 default:
141 abort();
142 }
143 }
144
145 static void instrument_expression(JSParseNode * node, FILE * f);
146 static void instrument_statement(JSParseNode * node, FILE * f, int indent);
147
148 static void instrument_function(JSParseNode * node, FILE * f, int indent) {
149 assert(node->pn_arity == PN_FUNC);
150 assert(ATOM_IS_OBJECT(node->pn_funAtom));
151 JSObject * object = ATOM_TO_OBJECT(node->pn_funAtom);
152 assert(JS_ObjectIsFunction(context, object));
153 JSFunction * function = (JSFunction *) JS_GetPrivate(context, object);
154 assert(function);
155 assert(object == function->object);
156 fprintf(f, "%*s", indent, "");
157 fprintf(f, "function");
158
159 /* function name */
160 if (function->atom) {
161 fputc(' ', f);
162 print_string_atom(function->atom, f);
163 }
164
165 /* function parameters */
166 fprintf(f, "(");
167 JSAtom ** params = xmalloc(function->nargs * sizeof(JSAtom *));
168 for (int i = 0; i < function->nargs; i++) {
169 /* initialize to NULL for sanity check */
170 params[i] = NULL;
171 }
172 JSScope * scope = OBJ_SCOPE(object);
173 for (JSScopeProperty * scope_property = SCOPE_LAST_PROP(scope); scope_property != NULL; scope_property = scope_property->parent) {
174 if (scope_property->getter != js_GetArgument) {
175 continue;
176 }
177 assert(scope_property->flags & SPROP_HAS_SHORTID);
178 assert((uint16) scope_property->shortid < function->nargs);
179 assert(JSID_IS_ATOM(scope_property->id));
180 params[(uint16) scope_property->shortid] = JSID_TO_ATOM(scope_property->id);
181 }
182 for (int i = 0; i < function->nargs; i++) {
183 assert(params[i] != NULL);
184 if (i > 0) {
185 fprintf(f, ", ");
186 }
187 if (ATOM_IS_STRING(params[i])) {
188 print_string_atom(params[i], f);
189 }
190 }
191 fprintf(f, ") {\n");
192 free(params);
193
194 /* function body */
195 instrument_statement(node->pn_body, f, indent + 2);
196
197 fprintf(f, "}\n");
198 }
199
200 static void instrument_function_call(JSParseNode * node, FILE * f) {
201 instrument_expression(node->pn_head, f);
202 fputc('(', f);
203 for (struct JSParseNode * p = node->pn_head->pn_next; p != NULL; p = p->pn_next) {
204 if (p != node->pn_head->pn_next) {
205 fprintf(f, ", ");
206 }
207 instrument_expression(p, f);
208 }
209 fputc(')', f);
210 }
211
212 /*
213 See <Expressions> in jsparse.h.
214 TOK_FUNCTION is handled as a statement and as an expression.
215 TOK_DBLDOT is not handled (XML op).
216 TOK_DEFSHARP and TOK_USESHARP are not handled.
217 TOK_ANYNAME is not handled (XML op).
218 TOK_AT is not handled (XML op).
219 TOK_DBLCOLON is not handled.
220 TOK_XML* are not handled.
221 There seem to be some undocumented expressions:
222 TOK_INSTANCEOF binary
223 TOK_IN binary
224 */
225 static void instrument_expression(JSParseNode * node, FILE * f) {
226 switch (node->pn_type) {
227 case TOK_FUNCTION:
228 instrument_function(node, f, 0);
229 break;
230 case TOK_COMMA:
231 for (struct JSParseNode * p = node->pn_head; p != NULL; p = p->pn_next) {
232 if (p != node->pn_head) {
233 fprintf(f, ", ");
234 }
235 instrument_expression(p, f);
236 }
237 break;
238 case TOK_ASSIGN:
239 instrument_expression(node->pn_left, f);
240 fputc(' ', f);
241 switch (node->pn_op) {
242 case JSOP_ADD:
243 case JSOP_SUB:
244 case JSOP_MUL:
245 case JSOP_MOD:
246 case JSOP_LSH:
247 case JSOP_RSH:
248 case JSOP_URSH:
249 case JSOP_BITAND:
250 case JSOP_BITOR:
251 case JSOP_BITXOR:
252 case JSOP_DIV:
253 fprintf(f, "%s", get_op(node->pn_op));
254 break;
255 default:
256 /* do nothing - it must be a simple assignment */
257 break;
258 }
259 fprintf(f, "= ");
260 instrument_expression(node->pn_right, f);
261 break;
262 case TOK_HOOK:
263 instrument_expression(node->pn_kid1, f);
264 fprintf(f, "? ");
265 instrument_expression(node->pn_kid2, f);
266 fprintf(f, ": ");
267 instrument_expression(node->pn_kid3, f);
268 break;
269 case TOK_OR:
270 instrument_expression(node->pn_left, f);
271 fprintf(f, " || ");
272 instrument_expression(node->pn_right, f);
273 break;
274 case TOK_AND:
275 instrument_expression(node->pn_left, f);
276 fprintf(f, " && ");
277 instrument_expression(node->pn_right, f);
278 break;
279 case TOK_BITOR:
280 case TOK_BITXOR:
281 case TOK_BITAND:
282 case TOK_EQOP:
283 case TOK_RELOP:
284 case TOK_SHOP:
285 case TOK_PLUS:
286 case TOK_MINUS:
287 case TOK_STAR:
288 case TOK_DIVOP:
289 switch (node->pn_arity) {
290 case PN_BINARY:
291 instrument_expression(node->pn_left, f);
292 fprintf(f, " %s ", get_op(node->pn_op));
293 instrument_expression(node->pn_right, f);
294 break;
295 case PN_LIST:
296 for (struct JSParseNode * p = node->pn_head; p != NULL; p = p->pn_next) {
297 if (p != node->pn_head) {
298 fprintf(f, " %s ", get_op(node->pn_op));
299 }
300 instrument_expression(p, f);
301 }
302 break;
303 default:
304 abort();
305 }
306 break;
307 case TOK_UNARYOP:
308 switch (node->pn_op) {
309 case JSOP_NEG:
310 fputc('-', f);
311 instrument_expression(node->pn_kid, f);
312 break;
313 case JSOP_POS:
314 fputc('+', f);
315 instrument_expression(node->pn_kid, f);
316 break;
317 case JSOP_NOT:
318 fputc('!', f);
319 instrument_expression(node->pn_kid, f);
320 break;
321 case JSOP_BITNOT:
322 fputc('~', f);
323 instrument_expression(node->pn_kid, f);
324 break;
325 case JSOP_TYPEOF:
326 fprintf(f, "typeof ");
327 instrument_expression(node->pn_kid, f);
328 break;
329 case JSOP_VOID:
330 fprintf(f, "void ");
331 instrument_expression(node->pn_kid, f);
332 break;
333 default:
334 abort();
335 break;
336 }
337 break;
338 case TOK_INC:
339 case TOK_DEC:
340 /*
341 This is not documented, but node->pn_op tells whether it is pre- or post-increment.
342 */
343 switch (node->pn_op) {
344 case JSOP_INCNAME:
345 case JSOP_INCPROP:
346 case JSOP_INCELEM:
347 fprintf(f, "++");
348 instrument_expression(node->pn_kid, f);
349 break;
350 case JSOP_DECNAME:
351 case JSOP_DECPROP:
352 case JSOP_DECELEM:
353 fprintf(f, "--");
354 instrument_expression(node->pn_kid, f);
355 break;
356 case JSOP_NAMEINC:
357 case JSOP_PROPINC:
358 case JSOP_ELEMINC:
359 instrument_expression(node->pn_kid, f);
360 fprintf(f, "++");
361 break;
362 case JSOP_NAMEDEC:
363 case JSOP_PROPDEC:
364 case JSOP_ELEMDEC:
365 instrument_expression(node->pn_kid, f);
366 fprintf(f, "--");
367 break;
368 default:
369 abort();
370 break;
371 }
372 break;
373 case TOK_NEW:
374 fprintf(f, "new ");
375 instrument_function_call(node, f);
376 break;
377 case TOK_DELETE:
378 fprintf(f, "delete ");
379 instrument_expression(node->pn_kid, f);
380 break;
381 case TOK_DOT:
382 /*
383 This may have originally been x['foo-bar']. Because the string 'foo-bar'
384 contains illegal characters, we have to use the subscript syntax instead of
385 the dot syntax.
386 */
387 instrument_expression(node->pn_expr, f);
388 /*
389 fputc('.', f);
390 print_string_atom(node->pn_atom, f);
391 */
392 fputc('[', f);
393 print_quoted_string_atom(node->pn_atom, f);
394 fputc(']', f);
395 break;
396 case TOK_LB:
397 instrument_expression(node->pn_left, f);
398 fputc('[', f);
399 instrument_expression(node->pn_right, f);
400 fputc(']', f);
401 break;
402 case TOK_LP:
403 instrument_function_call(node, f);
404 break;
405 case TOK_RB:
406 fputc('[', f);
407 for (struct JSParseNode * p = node->pn_head; p != NULL; p = p->pn_next) {
408 if (p != node->pn_head) {
409 fprintf(f, ", ");
410 }
411 /* TOK_COMMA is a special case: a hole in the array */
412 if (p->pn_type != TOK_COMMA) {
413 instrument_expression(p, f);
414 }
415 }
416 if (node->pn_extra == PNX_ENDCOMMA) {
417 fputc(',', f);
418 }
419 fputc(']', f);
420 break;
421 case TOK_RC:
422 fputc('{', f);
423 for (struct JSParseNode * p = node->pn_head; p != NULL; p = p->pn_next) {
424 assert(p->pn_type == TOK_COLON);
425 if (p != node->pn_head) {
426 fprintf(f, ", ");
427 }
428 instrument_expression(p->pn_left, f);
429 fprintf(f, ": ");
430 instrument_expression(p->pn_right, f);
431 }
432 fputc('}', f);
433 break;
434 case TOK_RP:
435 fputc('(', f);
436 instrument_expression(node->pn_kid, f);
437 fputc(')', f);
438 break;
439 case TOK_NAME:
440 print_string_atom(node->pn_atom, f);
441 break;
442 case TOK_STRING:
443 print_quoted_string_atom(node->pn_atom, f);
444 break;
445 case TOK_OBJECT:
446 switch (node->pn_op) {
447 case JSOP_OBJECT:
448 /* I assume this is JSOP_REGEXP */
449 abort();
450 break;
451 case JSOP_REGEXP:
452 assert(ATOM_IS_OBJECT(node->pn_atom));
453 {
454 JSObject * object = ATOM_TO_OBJECT(node->pn_atom);
455 jsval result;
456 js_regexp_toString(context, object, 0, NULL, &result);
457 print_string_jsval(result, f);
458 }
459 break;
460 default:
461 abort();
462 break;
463 }
464 break;
465 case TOK_NUMBER:
466 /*
467 A 64-bit IEEE 754 floating point number has a 52-bit fraction.
468 2^(-52) = 2.22 x 10^(-16)
469 Thus there are 16 significant digits.
470 To keep the output simple, special-case zero.
471 */
472 if (node->pn_dval == 0.0) {
473 fprintf(f, "0");
474 }
475 else {
476 fprintf(f, "%.15g", node->pn_dval);
477 }
478 break;
479 case TOK_PRIMARY:
480 switch (node->pn_op) {
481 case JSOP_TRUE:
482 fprintf(f, "true");
483 break;
484 case JSOP_FALSE:
485 fprintf(f, "false");
486 break;
487 case JSOP_NULL:
488 fprintf(f, "null");
489 break;
490 case JSOP_THIS:
491 fprintf(f, "this");
492 break;
493 /* jsscan.h mentions `super' ??? */
494 default:
495 abort();
496 }
497 break;
498 case TOK_INSTANCEOF:
499 instrument_expression(node->pn_left, f);
500 fprintf(f, " instanceof ");
501 instrument_expression(node->pn_right, f);
502 break;
503 case TOK_IN:
504 instrument_expression(node->pn_left, f);
505 fprintf(f, " in ");
506 instrument_expression(node->pn_right, f);
507 break;
508 default:
509 fatal("unsupported node type in file %s: %d", file_id, node->pn_type);
510 }
511 }
512
513 static void instrument_var_statement(JSParseNode * node, FILE * f, int indent) {
514 assert(node->pn_arity == PN_LIST);
515 fprintf(f, "%*s", indent, "");
516 fprintf(f, "var ");
517 for (struct JSParseNode * p = node->pn_u.list.head; p != NULL; p = p->pn_next) {
518 assert(p->pn_type == TOK_NAME);
519 assert(p->pn_arity == PN_NAME);
520 if (p != node->pn_head) {
521 fprintf(f, ", ");
522 }
523 print_string_atom(p->pn_atom, f);
524 if (p->pn_expr != NULL) {
525 fprintf(f, " = ");
526 instrument_expression(p->pn_expr, f);
527 }
528 }
529 }
530
531 static void output_statement(JSParseNode * node, FILE * f, int indent) {
532 switch (node->pn_type) {
533 case TOK_FUNCTION:
534 instrument_function(node, f, indent);
535 break;
536 case TOK_LC:
537 assert(node->pn_arity == PN_LIST);
538 /*
539 fprintf(f, "{\n");
540 */
541 for (struct JSParseNode * p = node->pn_u.list.head; p != NULL; p = p->pn_next) {
542 instrument_statement(p, f, indent);
543 }
544 /*
545 fprintf(f, "%*s", indent, "");
546 fprintf(f, "}\n");
547 */
548 break;
549 case TOK_IF:
550 assert(node->pn_arity == PN_TERNARY);
551 fprintf(f, "%*s", indent, "");
552 fprintf(f, "if (");
553 instrument_expression(node->pn_kid1, f);
554 fprintf(f, ") {\n");
555 instrument_statement(node->pn_kid2, f, indent + 2);
556 fprintf(f, "%*s", indent, "");
557 fprintf(f, "}\n");
558 if (node->pn_kid3) {
559 fprintf(f, "%*s", indent, "");
560 fprintf(f, "else {\n");
561 instrument_statement(node->pn_kid3, f, indent + 2);
562 fprintf(f, "%*s", indent, "");
563 fprintf(f, "}\n");
564 }
565 break;
566 case TOK_SWITCH:
567 assert(node->pn_arity == PN_BINARY);
568 fprintf(f, "%*s", indent, "");
569 fprintf(f, "switch (");
570 instrument_expression(node->pn_left, f);
571 fprintf(f, ") {\n");
572 for (struct JSParseNode * p = node->pn_right->pn_head; p != NULL; p = p->pn_next) {
573 fprintf(f, "%*s", indent, "");
574 switch (p->pn_type) {
575 case TOK_CASE:
576 fprintf(f, "case ");
577 instrument_expression(p->pn_left, f);
578 fprintf(f, ":\n");
579 break;
580 case TOK_DEFAULT:
581 fprintf(f, "default:\n");
582 break;
583 default:
584 abort();
585 break;
586 }
587 instrument_statement(p->pn_right, f, indent + 2);
588 }
589 fprintf(f, "%*s", indent, "");
590 fprintf(f, "}\n");
591 break;
592 case TOK_CASE:
593 case TOK_DEFAULT:
594 abort();
595 break;
596 case TOK_WHILE:
597 assert(node->pn_arity == PN_BINARY);
598 fprintf(f, "%*s", indent, "");
599 fprintf(f, "while (");
600 instrument_expression(node->pn_left, f);
601 fprintf(f, ") {\n");
602 instrument_statement(node->pn_right, f, indent + 2);
603 fprintf(f, "}\n");
604 break;
605 case TOK_DO:
606 assert(node->pn_arity == PN_BINARY);
607 fprintf(f, "%*s", indent, "");
608 fprintf(f, "do {\n");
609 instrument_statement(node->pn_left, f, indent + 2);
610 fprintf(f, "}\n");
611 fprintf(f, "%*s", indent, "");
612 fprintf(f, "while (");
613 instrument_expression(node->pn_right, f);
614 fprintf(f, ");\n");
615 break;
616 case TOK_FOR:
617 assert(node->pn_arity == PN_BINARY);
618 fprintf(f, "%*s", indent, "");
619 fprintf(f, "for (");
620 switch (node->pn_left->pn_type) {
621 case TOK_IN:
622 /* for/in */
623 assert(node->pn_left->pn_arity == PN_BINARY);
624 switch (node->pn_left->pn_left->pn_type) {
625 case TOK_VAR:
626 instrument_var_statement(node->pn_left->pn_left, f, 0);
627 break;
628 case TOK_NAME:
629 instrument_expression(node->pn_left->pn_left, f);
630 break;
631 default:
632 /* this is undocumented: for (x.value in y) */
633 instrument_expression(node->pn_left->pn_left, f);
634 break;
635 /*
636 default:
637 fprintf(stderr, "unexpected node type: %d\n", node->pn_left->pn_left->pn_type);
638 abort();
639 break;
640 */
641 }
642 fprintf(f, " in ");
643 instrument_expression(node->pn_left->pn_right, f);
644 break;
645 case TOK_RESERVED:
646 /* for (;;) */
647 assert(node->pn_left->pn_arity == PN_TERNARY);
648 if (node->pn_left->pn_kid1) {
649 if (node->pn_left->pn_kid1->pn_type == TOK_VAR) {
650 instrument_var_statement(node->pn_left->pn_kid1, f, 0);
651 }
652 else {
653 instrument_expression(node->pn_left->pn_kid1, f);
654 }
655 }
656 fprintf(f, ";");
657 if (node->pn_left->pn_kid2) {
658 fputc(' ', f);
659 instrument_expression(node->pn_left->pn_kid2, f);
660 }
661 fprintf(f, ";");
662 if (node->pn_left->pn_kid3) {
663 fputc(' ', f);
664 instrument_expression(node->pn_left->pn_kid3, f);
665 }
666 break;
667 default:
668 abort();
669 break;
670 }
671 fprintf(f, ") {\n");
672 instrument_statement(node->pn_right, f, indent + 2);
673 fprintf(f, "}\n");
674 break;
675 case TOK_THROW:
676 assert(node->pn_arity == PN_UNARY);
677 fprintf(f, "%*s", indent, "");
678 fprintf(f, "throw ");
679 instrument_expression(node->pn_u.unary.kid, f);
680 fprintf(f, ";\n");
681 break;
682 case TOK_TRY:
683 fprintf(f, "%*s", indent, "");
684 fprintf(f, "try {\n");
685 instrument_statement(node->pn_kid1, f, indent + 2);
686 fprintf(f, "%*s", indent, "");
687 fprintf(f, "}\n");
688 {
689 for (JSParseNode * catch = node->pn_kid2; catch != NULL; catch = catch->pn_kid2) {
690 assert(catch->pn_type == TOK_CATCH);
691 fprintf(f, "%*s", indent, "");
692 fprintf(f, "catch (");
693 assert(catch->pn_kid1->pn_arity == PN_NAME);
694 print_string_atom(catch->pn_kid1->pn_atom, f);
695 if (catch->pn_kid1->pn_expr) {
696 fprintf(f, " if ");
697 instrument_expression(catch->pn_kid1->pn_expr, f);
698 }
699 fprintf(f, ") {\n");
700 instrument_statement(catch->pn_kid3, f, indent + 2);
701 fprintf(f, "%*s", indent, "");
702 fprintf(f, "}\n");
703 }
704 }
705 if (node->pn_kid3) {
706 fprintf(f, "%*s", indent, "");
707 fprintf(f, "finally {\n");
708 instrument_statement(node->pn_kid3, f, indent + 2);
709 fprintf(f, "%*s", indent, "");
710 fprintf(f, "}\n");
711 }
712 break;
713 case TOK_CATCH:
714 abort();
715 break;
716 case TOK_BREAK:
717 case TOK_CONTINUE:
718 assert(node->pn_arity == PN_NAME || node->pn_arity == PN_NULLARY);
719 fprintf(f, "%*s", indent, "");
720 fputs(node->pn_type == TOK_BREAK? "break": "continue", f);
721 JSAtom * atom = node->pn_u.name.atom;
722 if (atom != NULL) {
723 fputc(' ', f);
724 print_string_atom(node->pn_atom, f);
725 }
726 fprintf(f, ";\n");
727 break;
728 case TOK_WITH:
729 assert(node->pn_arity == PN_BINARY);
730 fprintf(f, "%*s", indent, "");
731 fprintf(f, "with (");
732 instrument_expression(node->pn_left, f);
733 fprintf(f, ") {\n");
734 instrument_statement(node->pn_right, f, indent + 2);
735 fprintf(f, "%*s", indent, "");
736 fprintf(f, "}\n");
737 break;
738 case TOK_VAR:
739 instrument_var_statement(node, f, indent);
740 fprintf(f, ";\n");
741 break;
742 case TOK_RETURN:
743 assert(node->pn_arity == PN_UNARY);
744 fprintf(f, "%*s", indent, "");
745 fprintf(f, "return");
746 if (node->pn_kid != NULL) {
747 fprintf(f, " ");
748 instrument_expression(node->pn_kid, f);
749 }
750 fprintf(f, ";\n");
751 break;
752 case TOK_SEMI:
753 assert(node->pn_arity == PN_UNARY);
754 fprintf(f, "%*s", indent, "");
755 if (node->pn_kid != NULL) {
756 instrument_expression(node->pn_kid, f);
757 }
758 fprintf(f, ";\n");
759 break;
760 case TOK_COLON:
761 assert(node->pn_arity == PN_NAME);
762 /*
763 This one is tricky: can't output instrumentation between the label and the
764 statement it's supposed to label ...
765 */
766 fprintf(f, "%*s", indent < 2? 0: indent - 2, "");
767 print_string_atom(node->pn_atom, f);
768 fprintf(f, ":\n");
769 /*
770 ... use output_statement instead of instrument_statement.
771 */
772 output_statement(node->pn_expr, f, indent);
773 break;
774 default:
775 fatal("unsupported node type in file %s: %d", file_id, node->pn_type);
776 }
777 }
778
779 /*
780 See <Statements> in jsparse.h.
781 TOK_FUNCTION is handled as a statement and as an expression.
782 TOK_EXPORT, TOK_IMPORT are not handled.
783 */
784 static void instrument_statement(JSParseNode * node, FILE * f, int indent) {
785 if (node->pn_type != TOK_LC) {
786 int line = node->pn_pos.begin.lineno;
787 /* the root node has line number 0 */
788 if (line != 0) {
789 fprintf(f, "%*s", indent, "");
790 fprintf(f, "_$jscoverage['%s'][%d]++;\n", file_id, line);
791 lines[line - 1] = 1;
792 }
793 }
794 output_statement(node, f, indent);
795 }
796
797 static void instrument_js_stream(const char * id, int line, FILE * input, FILE * output) {
798 file_id = id;
799
800 /* scan the javascript */
801 JSTokenStream * token_stream = js_NewFileTokenStream(context, NULL, input);
802 if (token_stream == NULL) {
803 fatal("cannot create token stream from file: %s", file_id);
804 }
805
806 /* parse the javascript */
807 JSParseNode * node = js_ParseTokenStream(context, global, token_stream);
808 if (node == NULL) {
809 fatal("parse error in file: %s", file_id);
810 }
811 int num_lines = node->pn_pos.end.lineno;
812 lines = xmalloc(num_lines);
813 for (int i = 0; i < num_lines; i++) {
814 lines[i] = 0;
815 }
816
817 /*
818 Create a temporary file - we can't write directly to the output because we
819 need to know the line number info first.
820 */
821 FILE * temporary = tmpfile();
822 if (temporary == NULL) {
823 fatal("cannot create temporary file for script: %s", file_id);
824 }
825
826 /* write instrumented javascript to the temporary */
827 instrument_statement(node, temporary, 0);
828
829 /* write line number info to the output */
830 fprintf(output, "/* automatically generated by JSCoverage - do not edit */\n");
831 fprintf(output, "if (! top._$jscoverage) {\n top._$jscoverage = {};\n}\n");
832 fprintf(output, "var _$jscoverage = top._$jscoverage;\n");
833 fprintf(output, "if (! _$jscoverage['%s']) {\n", file_id);
834 fprintf(output, " _$jscoverage['%s'] = [];\n", file_id);
835 for (int i = 0; i < num_lines; i++) {
836 if (lines[i]) {
837 fprintf(output, " _$jscoverage['%s'][%d] = 0;\n", file_id, i + 1);
838 }
839 }
840 fprintf(output, "}\n");
841 lines = NULL;
842
843 /* copy the temporary to the output */
844 fseek(temporary, 0, SEEK_SET);
845 copy_stream(temporary, output);
846
847 fclose(temporary);
848
849 file_id = NULL;
850 }
851
852 void jscoverage_instrument_js(const char * id, FILE * input, FILE * output) {
853 instrument_js_stream(id, 0, input, output);
854 }

  ViewVC Help
Powered by ViewVC 1.1.24