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

Annotation of /trunk/instrument-js.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 95 - (hide annotations)
Wed May 7 22:50:00 2008 UTC (10 years, 7 months ago) by siliconforks
Original Path: trunk/instrument-js.c
File MIME type: text/plain
File size: 25801 byte(s)
Eliminate .jscoverage.js files.

1 siliconforks 2 /*
2     instrument-js.c - JavaScript instrumentation routines
3 siliconforks 87 Copyright (C) 2007, 2008 siliconforks.com
4 siliconforks 2
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 siliconforks 92 static void print_string(JSString * s, Stream * f) {
75 siliconforks 2 for (int i = 0; i < s->length; i++) {
76     char c = s->chars[i];
77 siliconforks 92 Stream_write_char(f, c);
78 siliconforks 2 }
79     }
80    
81 siliconforks 92 static void print_string_atom(JSAtom * atom, Stream * f) {
82 siliconforks 2 assert(ATOM_IS_STRING(atom));
83     JSString * s = ATOM_TO_STRING(atom);
84     print_string(s, f);
85     }
86    
87 siliconforks 92 static void print_string_jsval(jsval value, Stream * f) {
88 siliconforks 2 assert(JSVAL_IS_STRING(value));
89     JSString * s = JSVAL_TO_STRING(value);
90     print_string(s, f);
91     }
92    
93 siliconforks 92 static void print_quoted_string_atom(JSAtom * atom, Stream * f) {
94 siliconforks 2 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 siliconforks 92 static void instrument_expression(JSParseNode * node, Stream * f);
146     static void instrument_statement(JSParseNode * node, Stream * f, int indent);
147 siliconforks 2
148 siliconforks 92 static void instrument_function(JSParseNode * node, Stream * f, int indent) {
149 siliconforks 2 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 siliconforks 92 Stream_printf(f, "%*s", indent, "");
157     Stream_write_string(f, "function");
158 siliconforks 2
159     /* function name */
160     if (function->atom) {
161 siliconforks 92 Stream_write_char(f, ' ');
162 siliconforks 2 print_string_atom(function->atom, f);
163     }
164    
165     /* function parameters */
166 siliconforks 92 Stream_write_string(f, "(");
167 siliconforks 2 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 siliconforks 92 Stream_write_string(f, ", ");
186 siliconforks 2 }
187     if (ATOM_IS_STRING(params[i])) {
188     print_string_atom(params[i], f);
189     }
190     }
191 siliconforks 92 Stream_write_string(f, ") {\n");
192 siliconforks 2 free(params);
193    
194     /* function body */
195     instrument_statement(node->pn_body, f, indent + 2);
196    
197 siliconforks 92 Stream_write_string(f, "}\n");
198 siliconforks 2 }
199    
200 siliconforks 92 static void instrument_function_call(JSParseNode * node, Stream * f) {
201 siliconforks 2 instrument_expression(node->pn_head, f);
202 siliconforks 92 Stream_write_char(f, '(');
203 siliconforks 2 for (struct JSParseNode * p = node->pn_head->pn_next; p != NULL; p = p->pn_next) {
204     if (p != node->pn_head->pn_next) {
205 siliconforks 92 Stream_write_string(f, ", ");
206 siliconforks 2 }
207     instrument_expression(p, f);
208     }
209 siliconforks 92 Stream_write_char(f, ')');
210 siliconforks 2 }
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 siliconforks 92 static void instrument_expression(JSParseNode * node, Stream * f) {
226 siliconforks 2 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 siliconforks 92 Stream_write_string(f, ", ");
234 siliconforks 2 }
235     instrument_expression(p, f);
236     }
237     break;
238     case TOK_ASSIGN:
239     instrument_expression(node->pn_left, f);
240 siliconforks 92 Stream_write_char(f, ' ');
241 siliconforks 2 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 siliconforks 92 Stream_printf(f, "%s", get_op(node->pn_op));
254 siliconforks 2 break;
255     default:
256     /* do nothing - it must be a simple assignment */
257     break;
258     }
259 siliconforks 92 Stream_write_string(f, "= ");
260 siliconforks 2 instrument_expression(node->pn_right, f);
261     break;
262     case TOK_HOOK:
263     instrument_expression(node->pn_kid1, f);
264 siliconforks 92 Stream_write_string(f, "? ");
265 siliconforks 2 instrument_expression(node->pn_kid2, f);
266 siliconforks 92 Stream_write_string(f, ": ");
267 siliconforks 2 instrument_expression(node->pn_kid3, f);
268     break;
269     case TOK_OR:
270     instrument_expression(node->pn_left, f);
271 siliconforks 92 Stream_write_string(f, " || ");
272 siliconforks 2 instrument_expression(node->pn_right, f);
273     break;
274     case TOK_AND:
275     instrument_expression(node->pn_left, f);
276 siliconforks 92 Stream_write_string(f, " && ");
277 siliconforks 2 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 siliconforks 92 Stream_printf(f, " %s ", get_op(node->pn_op));
293 siliconforks 2 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 siliconforks 92 Stream_printf(f, " %s ", get_op(node->pn_op));
299 siliconforks 2 }
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 siliconforks 92 Stream_write_char(f, '-');
311 siliconforks 2 instrument_expression(node->pn_kid, f);
312     break;
313     case JSOP_POS:
314 siliconforks 92 Stream_write_char(f, '+');
315 siliconforks 2 instrument_expression(node->pn_kid, f);
316     break;
317     case JSOP_NOT:
318 siliconforks 92 Stream_write_char(f, '!');
319 siliconforks 2 instrument_expression(node->pn_kid, f);
320     break;
321     case JSOP_BITNOT:
322 siliconforks 92 Stream_write_char(f, '~');
323 siliconforks 2 instrument_expression(node->pn_kid, f);
324     break;
325     case JSOP_TYPEOF:
326 siliconforks 92 Stream_write_string(f, "typeof ");
327 siliconforks 2 instrument_expression(node->pn_kid, f);
328     break;
329     case JSOP_VOID:
330 siliconforks 92 Stream_write_string(f, "void ");
331 siliconforks 2 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 siliconforks 92 Stream_write_string(f, "++");
348 siliconforks 2 instrument_expression(node->pn_kid, f);
349     break;
350     case JSOP_DECNAME:
351     case JSOP_DECPROP:
352     case JSOP_DECELEM:
353 siliconforks 92 Stream_write_string(f, "--");
354 siliconforks 2 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 siliconforks 92 Stream_write_string(f, "++");
361 siliconforks 2 break;
362     case JSOP_NAMEDEC:
363     case JSOP_PROPDEC:
364     case JSOP_ELEMDEC:
365     instrument_expression(node->pn_kid, f);
366 siliconforks 92 Stream_write_string(f, "--");
367 siliconforks 2 break;
368     default:
369     abort();
370     break;
371     }
372     break;
373     case TOK_NEW:
374 siliconforks 92 Stream_write_string(f, "new ");
375 siliconforks 2 instrument_function_call(node, f);
376     break;
377     case TOK_DELETE:
378 siliconforks 92 Stream_write_string(f, "delete ");
379 siliconforks 2 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 siliconforks 84 assert(ATOM_IS_STRING(node->pn_atom));
389     {
390     JSString * s = ATOM_TO_STRING(node->pn_atom);
391     /* XXX - semantics changed in 1.7 */
392     if (! ATOM_KEYWORD(node->pn_atom) && js_IsIdentifier(s)) {
393 siliconforks 92 Stream_write_char(f, '.');
394 siliconforks 84 print_string_atom(node->pn_atom, f);
395     }
396     else {
397 siliconforks 92 Stream_write_char(f, '[');
398 siliconforks 84 print_quoted_string_atom(node->pn_atom, f);
399 siliconforks 92 Stream_write_char(f, ']');
400 siliconforks 84 }
401     }
402 siliconforks 2 break;
403     case TOK_LB:
404     instrument_expression(node->pn_left, f);
405 siliconforks 92 Stream_write_char(f, '[');
406 siliconforks 2 instrument_expression(node->pn_right, f);
407 siliconforks 92 Stream_write_char(f, ']');
408 siliconforks 2 break;
409     case TOK_LP:
410     instrument_function_call(node, f);
411     break;
412     case TOK_RB:
413 siliconforks 92 Stream_write_char(f, '[');
414 siliconforks 2 for (struct JSParseNode * p = node->pn_head; p != NULL; p = p->pn_next) {
415     if (p != node->pn_head) {
416 siliconforks 92 Stream_write_string(f, ", ");
417 siliconforks 2 }
418     /* TOK_COMMA is a special case: a hole in the array */
419     if (p->pn_type != TOK_COMMA) {
420     instrument_expression(p, f);
421     }
422     }
423     if (node->pn_extra == PNX_ENDCOMMA) {
424 siliconforks 92 Stream_write_char(f, ',');
425 siliconforks 2 }
426 siliconforks 92 Stream_write_char(f, ']');
427 siliconforks 2 break;
428     case TOK_RC:
429 siliconforks 92 Stream_write_char(f, '{');
430 siliconforks 2 for (struct JSParseNode * p = node->pn_head; p != NULL; p = p->pn_next) {
431     assert(p->pn_type == TOK_COLON);
432     if (p != node->pn_head) {
433 siliconforks 92 Stream_write_string(f, ", ");
434 siliconforks 2 }
435     instrument_expression(p->pn_left, f);
436 siliconforks 92 Stream_write_string(f, ": ");
437 siliconforks 2 instrument_expression(p->pn_right, f);
438     }
439 siliconforks 92 Stream_write_char(f, '}');
440 siliconforks 2 break;
441     case TOK_RP:
442 siliconforks 92 Stream_write_char(f, '(');
443 siliconforks 2 instrument_expression(node->pn_kid, f);
444 siliconforks 92 Stream_write_char(f, ')');
445 siliconforks 2 break;
446     case TOK_NAME:
447     print_string_atom(node->pn_atom, f);
448     break;
449     case TOK_STRING:
450     print_quoted_string_atom(node->pn_atom, f);
451     break;
452     case TOK_OBJECT:
453     switch (node->pn_op) {
454     case JSOP_OBJECT:
455     /* I assume this is JSOP_REGEXP */
456     abort();
457     break;
458     case JSOP_REGEXP:
459     assert(ATOM_IS_OBJECT(node->pn_atom));
460     {
461     JSObject * object = ATOM_TO_OBJECT(node->pn_atom);
462     jsval result;
463     js_regexp_toString(context, object, 0, NULL, &result);
464     print_string_jsval(result, f);
465     }
466     break;
467     default:
468     abort();
469     break;
470     }
471     break;
472     case TOK_NUMBER:
473     /*
474     A 64-bit IEEE 754 floating point number has a 52-bit fraction.
475     2^(-52) = 2.22 x 10^(-16)
476     Thus there are 16 significant digits.
477     To keep the output simple, special-case zero.
478     */
479     if (node->pn_dval == 0.0) {
480 siliconforks 92 Stream_write_string(f, "0");
481 siliconforks 2 }
482     else {
483 siliconforks 92 Stream_printf(f, "%.15g", node->pn_dval);
484 siliconforks 2 }
485     break;
486     case TOK_PRIMARY:
487     switch (node->pn_op) {
488     case JSOP_TRUE:
489 siliconforks 92 Stream_write_string(f, "true");
490 siliconforks 2 break;
491     case JSOP_FALSE:
492 siliconforks 92 Stream_write_string(f, "false");
493 siliconforks 2 break;
494     case JSOP_NULL:
495 siliconforks 92 Stream_write_string(f, "null");
496 siliconforks 2 break;
497     case JSOP_THIS:
498 siliconforks 92 Stream_write_string(f, "this");
499 siliconforks 2 break;
500     /* jsscan.h mentions `super' ??? */
501     default:
502     abort();
503     }
504     break;
505     case TOK_INSTANCEOF:
506     instrument_expression(node->pn_left, f);
507 siliconforks 92 Stream_write_string(f, " instanceof ");
508 siliconforks 2 instrument_expression(node->pn_right, f);
509     break;
510     case TOK_IN:
511     instrument_expression(node->pn_left, f);
512 siliconforks 92 Stream_write_string(f, " in ");
513 siliconforks 2 instrument_expression(node->pn_right, f);
514     break;
515     default:
516     fatal("unsupported node type in file %s: %d", file_id, node->pn_type);
517     }
518     }
519    
520 siliconforks 92 static void instrument_var_statement(JSParseNode * node, Stream * f, int indent) {
521 siliconforks 2 assert(node->pn_arity == PN_LIST);
522 siliconforks 92 Stream_printf(f, "%*s", indent, "");
523     Stream_write_string(f, "var ");
524 siliconforks 2 for (struct JSParseNode * p = node->pn_u.list.head; p != NULL; p = p->pn_next) {
525     assert(p->pn_type == TOK_NAME);
526     assert(p->pn_arity == PN_NAME);
527     if (p != node->pn_head) {
528 siliconforks 92 Stream_write_string(f, ", ");
529 siliconforks 2 }
530     print_string_atom(p->pn_atom, f);
531     if (p->pn_expr != NULL) {
532 siliconforks 92 Stream_write_string(f, " = ");
533 siliconforks 2 instrument_expression(p->pn_expr, f);
534     }
535     }
536     }
537    
538 siliconforks 92 static void output_statement(JSParseNode * node, Stream * f, int indent) {
539 siliconforks 2 switch (node->pn_type) {
540     case TOK_FUNCTION:
541     instrument_function(node, f, indent);
542     break;
543     case TOK_LC:
544     assert(node->pn_arity == PN_LIST);
545     /*
546 siliconforks 92 Stream_write_string(f, "{\n");
547 siliconforks 2 */
548     for (struct JSParseNode * p = node->pn_u.list.head; p != NULL; p = p->pn_next) {
549     instrument_statement(p, f, indent);
550     }
551     /*
552 siliconforks 92 Stream_printf(f, "%*s", indent, "");
553     Stream_write_string(f, "}\n");
554 siliconforks 2 */
555     break;
556     case TOK_IF:
557     assert(node->pn_arity == PN_TERNARY);
558 siliconforks 92 Stream_printf(f, "%*s", indent, "");
559     Stream_write_string(f, "if (");
560 siliconforks 2 instrument_expression(node->pn_kid1, f);
561 siliconforks 92 Stream_write_string(f, ") {\n");
562 siliconforks 2 instrument_statement(node->pn_kid2, f, indent + 2);
563 siliconforks 92 Stream_printf(f, "%*s", indent, "");
564     Stream_write_string(f, "}\n");
565 siliconforks 2 if (node->pn_kid3) {
566 siliconforks 92 Stream_printf(f, "%*s", indent, "");
567     Stream_write_string(f, "else {\n");
568 siliconforks 2 instrument_statement(node->pn_kid3, f, indent + 2);
569 siliconforks 92 Stream_printf(f, "%*s", indent, "");
570     Stream_write_string(f, "}\n");
571 siliconforks 2 }
572     break;
573     case TOK_SWITCH:
574     assert(node->pn_arity == PN_BINARY);
575 siliconforks 92 Stream_printf(f, "%*s", indent, "");
576     Stream_write_string(f, "switch (");
577 siliconforks 2 instrument_expression(node->pn_left, f);
578 siliconforks 92 Stream_write_string(f, ") {\n");
579 siliconforks 2 for (struct JSParseNode * p = node->pn_right->pn_head; p != NULL; p = p->pn_next) {
580 siliconforks 92 Stream_printf(f, "%*s", indent, "");
581 siliconforks 2 switch (p->pn_type) {
582     case TOK_CASE:
583 siliconforks 92 Stream_write_string(f, "case ");
584 siliconforks 2 instrument_expression(p->pn_left, f);
585 siliconforks 92 Stream_write_string(f, ":\n");
586 siliconforks 2 break;
587     case TOK_DEFAULT:
588 siliconforks 92 Stream_write_string(f, "default:\n");
589 siliconforks 2 break;
590     default:
591     abort();
592     break;
593     }
594     instrument_statement(p->pn_right, f, indent + 2);
595     }
596 siliconforks 92 Stream_printf(f, "%*s", indent, "");
597     Stream_write_string(f, "}\n");
598 siliconforks 2 break;
599     case TOK_CASE:
600     case TOK_DEFAULT:
601     abort();
602     break;
603     case TOK_WHILE:
604     assert(node->pn_arity == PN_BINARY);
605 siliconforks 92 Stream_printf(f, "%*s", indent, "");
606     Stream_write_string(f, "while (");
607 siliconforks 2 instrument_expression(node->pn_left, f);
608 siliconforks 92 Stream_write_string(f, ") {\n");
609 siliconforks 2 instrument_statement(node->pn_right, f, indent + 2);
610 siliconforks 92 Stream_write_string(f, "}\n");
611 siliconforks 2 break;
612     case TOK_DO:
613     assert(node->pn_arity == PN_BINARY);
614 siliconforks 92 Stream_printf(f, "%*s", indent, "");
615     Stream_write_string(f, "do {\n");
616 siliconforks 2 instrument_statement(node->pn_left, f, indent + 2);
617 siliconforks 92 Stream_write_string(f, "}\n");
618     Stream_printf(f, "%*s", indent, "");
619     Stream_write_string(f, "while (");
620 siliconforks 2 instrument_expression(node->pn_right, f);
621 siliconforks 92 Stream_write_string(f, ");\n");
622 siliconforks 2 break;
623     case TOK_FOR:
624     assert(node->pn_arity == PN_BINARY);
625 siliconforks 92 Stream_printf(f, "%*s", indent, "");
626     Stream_write_string(f, "for (");
627 siliconforks 2 switch (node->pn_left->pn_type) {
628     case TOK_IN:
629     /* for/in */
630     assert(node->pn_left->pn_arity == PN_BINARY);
631     switch (node->pn_left->pn_left->pn_type) {
632     case TOK_VAR:
633     instrument_var_statement(node->pn_left->pn_left, f, 0);
634     break;
635     case TOK_NAME:
636     instrument_expression(node->pn_left->pn_left, f);
637     break;
638     default:
639     /* this is undocumented: for (x.value in y) */
640     instrument_expression(node->pn_left->pn_left, f);
641     break;
642     /*
643     default:
644     fprintf(stderr, "unexpected node type: %d\n", node->pn_left->pn_left->pn_type);
645     abort();
646     break;
647     */
648     }
649 siliconforks 92 Stream_write_string(f, " in ");
650 siliconforks 2 instrument_expression(node->pn_left->pn_right, f);
651     break;
652     case TOK_RESERVED:
653     /* for (;;) */
654     assert(node->pn_left->pn_arity == PN_TERNARY);
655     if (node->pn_left->pn_kid1) {
656     if (node->pn_left->pn_kid1->pn_type == TOK_VAR) {
657     instrument_var_statement(node->pn_left->pn_kid1, f, 0);
658     }
659     else {
660     instrument_expression(node->pn_left->pn_kid1, f);
661     }
662     }
663 siliconforks 92 Stream_write_string(f, ";");
664 siliconforks 2 if (node->pn_left->pn_kid2) {
665 siliconforks 92 Stream_write_char(f, ' ');
666 siliconforks 2 instrument_expression(node->pn_left->pn_kid2, f);
667     }
668 siliconforks 92 Stream_write_string(f, ";");
669 siliconforks 2 if (node->pn_left->pn_kid3) {
670 siliconforks 92 Stream_write_char(f, ' ');
671 siliconforks 2 instrument_expression(node->pn_left->pn_kid3, f);
672     }
673     break;
674     default:
675     abort();
676     break;
677     }
678 siliconforks 92 Stream_write_string(f, ") {\n");
679 siliconforks 2 instrument_statement(node->pn_right, f, indent + 2);
680 siliconforks 92 Stream_write_string(f, "}\n");
681 siliconforks 2 break;
682     case TOK_THROW:
683     assert(node->pn_arity == PN_UNARY);
684 siliconforks 92 Stream_printf(f, "%*s", indent, "");
685     Stream_write_string(f, "throw ");
686 siliconforks 2 instrument_expression(node->pn_u.unary.kid, f);
687 siliconforks 92 Stream_write_string(f, ";\n");
688 siliconforks 2 break;
689     case TOK_TRY:
690 siliconforks 92 Stream_printf(f, "%*s", indent, "");
691     Stream_write_string(f, "try {\n");
692 siliconforks 2 instrument_statement(node->pn_kid1, f, indent + 2);
693 siliconforks 92 Stream_printf(f, "%*s", indent, "");
694     Stream_write_string(f, "}\n");
695 siliconforks 2 {
696     for (JSParseNode * catch = node->pn_kid2; catch != NULL; catch = catch->pn_kid2) {
697     assert(catch->pn_type == TOK_CATCH);
698 siliconforks 92 Stream_printf(f, "%*s", indent, "");
699     Stream_write_string(f, "catch (");
700 siliconforks 2 assert(catch->pn_kid1->pn_arity == PN_NAME);
701     print_string_atom(catch->pn_kid1->pn_atom, f);
702     if (catch->pn_kid1->pn_expr) {
703 siliconforks 92 Stream_write_string(f, " if ");
704 siliconforks 2 instrument_expression(catch->pn_kid1->pn_expr, f);
705     }
706 siliconforks 92 Stream_write_string(f, ") {\n");
707 siliconforks 2 instrument_statement(catch->pn_kid3, f, indent + 2);
708 siliconforks 92 Stream_printf(f, "%*s", indent, "");
709     Stream_write_string(f, "}\n");
710 siliconforks 2 }
711     }
712     if (node->pn_kid3) {
713 siliconforks 92 Stream_printf(f, "%*s", indent, "");
714     Stream_write_string(f, "finally {\n");
715 siliconforks 2 instrument_statement(node->pn_kid3, f, indent + 2);
716 siliconforks 92 Stream_printf(f, "%*s", indent, "");
717     Stream_write_string(f, "}\n");
718 siliconforks 2 }
719     break;
720     case TOK_CATCH:
721     abort();
722     break;
723     case TOK_BREAK:
724     case TOK_CONTINUE:
725     assert(node->pn_arity == PN_NAME || node->pn_arity == PN_NULLARY);
726 siliconforks 92 Stream_printf(f, "%*s", indent, "");
727     Stream_write_string(f, node->pn_type == TOK_BREAK? "break": "continue");
728 siliconforks 2 JSAtom * atom = node->pn_u.name.atom;
729     if (atom != NULL) {
730 siliconforks 92 Stream_write_char(f, ' ');
731 siliconforks 2 print_string_atom(node->pn_atom, f);
732     }
733 siliconforks 92 Stream_write_string(f, ";\n");
734 siliconforks 2 break;
735     case TOK_WITH:
736     assert(node->pn_arity == PN_BINARY);
737 siliconforks 92 Stream_printf(f, "%*s", indent, "");
738     Stream_write_string(f, "with (");
739 siliconforks 2 instrument_expression(node->pn_left, f);
740 siliconforks 92 Stream_write_string(f, ") {\n");
741 siliconforks 2 instrument_statement(node->pn_right, f, indent + 2);
742 siliconforks 92 Stream_printf(f, "%*s", indent, "");
743     Stream_write_string(f, "}\n");
744 siliconforks 2 break;
745     case TOK_VAR:
746     instrument_var_statement(node, f, indent);
747 siliconforks 92 Stream_write_string(f, ";\n");
748 siliconforks 2 break;
749     case TOK_RETURN:
750     assert(node->pn_arity == PN_UNARY);
751 siliconforks 92 Stream_printf(f, "%*s", indent, "");
752     Stream_write_string(f, "return");
753 siliconforks 2 if (node->pn_kid != NULL) {
754 siliconforks 92 Stream_write_char(f, ' ');
755 siliconforks 2 instrument_expression(node->pn_kid, f);
756     }
757 siliconforks 92 Stream_write_string(f, ";\n");
758 siliconforks 2 break;
759     case TOK_SEMI:
760     assert(node->pn_arity == PN_UNARY);
761 siliconforks 92 Stream_printf(f, "%*s", indent, "");
762 siliconforks 2 if (node->pn_kid != NULL) {
763     instrument_expression(node->pn_kid, f);
764     }
765 siliconforks 92 Stream_write_string(f, ";\n");
766 siliconforks 2 break;
767     case TOK_COLON:
768     assert(node->pn_arity == PN_NAME);
769     /*
770     This one is tricky: can't output instrumentation between the label and the
771     statement it's supposed to label ...
772     */
773 siliconforks 92 Stream_printf(f, "%*s", indent < 2? 0: indent - 2, "");
774 siliconforks 2 print_string_atom(node->pn_atom, f);
775 siliconforks 92 Stream_write_string(f, ":\n");
776 siliconforks 2 /*
777     ... use output_statement instead of instrument_statement.
778     */
779     output_statement(node->pn_expr, f, indent);
780     break;
781     default:
782     fatal("unsupported node type in file %s: %d", file_id, node->pn_type);
783     }
784     }
785    
786     /*
787     See <Statements> in jsparse.h.
788     TOK_FUNCTION is handled as a statement and as an expression.
789     TOK_EXPORT, TOK_IMPORT are not handled.
790     */
791 siliconforks 92 static void instrument_statement(JSParseNode * node, Stream * f, int indent) {
792 siliconforks 2 if (node->pn_type != TOK_LC) {
793     int line = node->pn_pos.begin.lineno;
794     /* the root node has line number 0 */
795     if (line != 0) {
796 siliconforks 92 Stream_printf(f, "%*s", indent, "");
797     Stream_printf(f, "_$jscoverage['%s'][%d]++;\n", file_id, line);
798 siliconforks 2 lines[line - 1] = 1;
799     }
800     }
801     output_statement(node, f, indent);
802     }
803    
804 siliconforks 92 void jscoverage_instrument_js(const char * id, Stream * input, Stream * output) {
805 siliconforks 2 file_id = id;
806    
807     /* scan the javascript */
808 siliconforks 92 size_t input_length = input->length;
809     jschar * base = js_InflateString(context, input->data, &input_length);
810     if (base == NULL) {
811     fatal("out of memory");
812     }
813     JSTokenStream * token_stream = js_NewTokenStream(context, base, input_length, NULL, 1, NULL);
814 siliconforks 2 if (token_stream == NULL) {
815     fatal("cannot create token stream from file: %s", file_id);
816     }
817    
818     /* parse the javascript */
819     JSParseNode * node = js_ParseTokenStream(context, global, token_stream);
820     if (node == NULL) {
821     fatal("parse error in file: %s", file_id);
822     }
823     int num_lines = node->pn_pos.end.lineno;
824     lines = xmalloc(num_lines);
825     for (int i = 0; i < num_lines; i++) {
826     lines[i] = 0;
827     }
828    
829     /*
830 siliconforks 92 An instrumented JavaScript file has 3 sections:
831     1. initialization
832     2. instrumented source code
833     3. original source code (TODO)
834 siliconforks 2 */
835    
836 siliconforks 92 Stream * instrumented = Stream_new(0);
837     instrument_statement(node, instrumented, 0);
838 siliconforks 2
839     /* write line number info to the output */
840 siliconforks 92 Stream_write_string(output, "/* automatically generated by JSCoverage - do not edit */\n");
841     Stream_write_string(output, "if (! top._$jscoverage) {\n top._$jscoverage = {};\n}\n");
842     Stream_write_string(output, "var _$jscoverage = top._$jscoverage;\n");
843     Stream_printf(output, "if (! _$jscoverage['%s']) {\n", file_id);
844     Stream_printf(output, " _$jscoverage['%s'] = [];\n", file_id);
845 siliconforks 2 for (int i = 0; i < num_lines; i++) {
846     if (lines[i]) {
847 siliconforks 92 Stream_printf(output, " _$jscoverage['%s'][%d] = 0;\n", file_id, i + 1);
848 siliconforks 2 }
849     }
850 siliconforks 92 Stream_write_string(output, "}\n");
851 siliconforks 90 free(lines);
852 siliconforks 2 lines = NULL;
853    
854 siliconforks 92 /* copy the instrumented source code to the output */
855     Stream_write(output, instrumented->data, instrumented->length);
856 siliconforks 95 Stream_write_char(output, '\n');
857 siliconforks 2
858 siliconforks 95 /* copy the original source to the output */
859     size_t i = 0;
860     while (i < input_length) {
861     Stream_write_string(output, "// ");
862     size_t line_start = i;
863     while (i < input_length && base[i] != '\r' && base[i] != '\n') {
864     i++;
865     }
866    
867     size_t line_end = i;
868     if (i < input_length) {
869     if (base[i] == '\r') {
870     line_end = i;
871     i++;
872     if (i < input_length && base[i] == '\n') {
873     i++;
874     }
875     }
876     else if (base[i] == '\n') {
877     line_end = i;
878     i++;
879     }
880     else {
881     abort();
882     }
883     }
884    
885     char * line = js_DeflateString(context, base + line_start, line_end - line_start);
886     Stream_write_string(output, line);
887     Stream_write_char(output, '\n');
888     JS_free(context, line);
889     }
890    
891 siliconforks 92 Stream_delete(instrumented);
892 siliconforks 2
893 siliconforks 92 JS_free(context, base);
894    
895 siliconforks 2 file_id = NULL;
896     }

  ViewVC Help
Powered by ViewVC 1.1.24