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

Annotation of /trunk/instrument-js.c

Parent Directory Parent Directory | Revision Log Revision Log


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

1 siliconforks 2 /*
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