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

Annotation of /trunk/instrument-js.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 345 - (hide annotations)
Fri Oct 24 16:16:00 2008 UTC (10 years, 11 months ago) by siliconforks
File MIME type: text/plain
File size: 49287 byte(s)
Fix double-counting of lines with expression closures.
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 siliconforks 116 #include <config.h>
21    
22 siliconforks 2 #include "instrument-js.h"
23    
24     #include <assert.h>
25     #include <stdlib.h>
26     #include <string.h>
27    
28     #include <jsapi.h>
29 siliconforks 336 #include <jsarena.h>
30 siliconforks 2 #include <jsatom.h>
31 siliconforks 284 #include <jsexn.h>
32 siliconforks 2 #include <jsfun.h>
33     #include <jsinterp.h>
34 siliconforks 339 #include <jsiter.h>
35 siliconforks 2 #include <jsparse.h>
36     #include <jsregexp.h>
37     #include <jsscope.h>
38     #include <jsstr.h>
39    
40 siliconforks 174 #include "encoding.h"
41 siliconforks 179 #include "global.h"
42     #include "highlight.h"
43 siliconforks 116 #include "resource-manager.h"
44 siliconforks 2 #include "util.h"
45    
46 siliconforks 240 struct IfDirective {
47     const jschar * condition_start;
48     const jschar * condition_end;
49     uint16_t start_line;
50     uint16_t end_line;
51     struct IfDirective * next;
52     };
53    
54     static bool * exclusive_directives = NULL;
55    
56 siliconforks 2 static JSRuntime * runtime = NULL;
57     static JSContext * context = NULL;
58     static JSObject * global = NULL;
59 siliconforks 339 static JSVersion js_version = JSVERSION_ECMA_3;
60 siliconforks 2
61     /*
62     JSParseNode objects store line numbers starting from 1.
63     The lines array stores line numbers starting from 0.
64     */
65     static const char * file_id = NULL;
66     static char * lines = NULL;
67 siliconforks 214 static uint16_t num_lines = 0;
68 siliconforks 2
69 siliconforks 339 void jscoverage_set_js_version(const char * version) {
70     js_version = atoi(version);
71     }
72    
73 siliconforks 2 void jscoverage_init(void) {
74     runtime = JS_NewRuntime(8L * 1024L * 1024L);
75     if (runtime == NULL) {
76     fatal("cannot create runtime");
77     }
78    
79     context = JS_NewContext(runtime, 8192);
80     if (context == NULL) {
81     fatal("cannot create context");
82     }
83    
84 siliconforks 339 JS_SetVersion(context, js_version);
85    
86 siliconforks 2 global = JS_NewObject(context, NULL, NULL, NULL);
87     if (global == NULL) {
88     fatal("cannot create global object");
89     }
90    
91     if (! JS_InitStandardClasses(context, global)) {
92     fatal("cannot initialize standard classes");
93     }
94     }
95    
96     void jscoverage_cleanup(void) {
97     JS_DestroyContext(context);
98     JS_DestroyRuntime(runtime);
99     }
100    
101 siliconforks 246 static void print_javascript(const jschar * characters, size_t num_characters, Stream * f) {
102     for (size_t i = 0; i < num_characters; i++) {
103     jschar c = characters[i];
104     /*
105     XXX does not handle no-break space, other unicode "space separator"
106     */
107     switch (c) {
108     case 0x9:
109     case 0xB:
110     case 0xC:
111     Stream_write_char(f, c);
112     break;
113     default:
114     if (32 <= c && c <= 126) {
115     Stream_write_char(f, c);
116     }
117     else {
118     Stream_printf(f, "\\u%04x", c);
119     }
120     break;
121     }
122     }
123     }
124    
125 siliconforks 92 static void print_string(JSString * s, Stream * f) {
126 siliconforks 174 size_t length = JSSTRING_LENGTH(s);
127     jschar * characters = JSSTRING_CHARS(s);
128     for (size_t i = 0; i < length; i++) {
129     jschar c = characters[i];
130     if (32 <= c && c <= 126) {
131     switch (c) {
132     case '"':
133     Stream_write_string(f, "\\\"");
134     break;
135     /*
136     case '\'':
137     Stream_write_string(f, "\\'");
138     break;
139     */
140     case '\\':
141     Stream_write_string(f, "\\\\");
142     break;
143     default:
144     Stream_write_char(f, c);
145     break;
146     }
147     }
148     else {
149     switch (c) {
150     case 0x8:
151     Stream_write_string(f, "\\b");
152     break;
153     case 0x9:
154     Stream_write_string(f, "\\t");
155     break;
156     case 0xa:
157     Stream_write_string(f, "\\n");
158     break;
159 siliconforks 317 /* IE doesn't support this */
160     /*
161 siliconforks 174 case 0xb:
162     Stream_write_string(f, "\\v");
163     break;
164 siliconforks 317 */
165 siliconforks 174 case 0xc:
166     Stream_write_string(f, "\\f");
167     break;
168     case 0xd:
169     Stream_write_string(f, "\\r");
170     break;
171     default:
172     Stream_printf(f, "\\u%04x", c);
173     break;
174     }
175     }
176 siliconforks 2 }
177     }
178    
179 siliconforks 92 static void print_string_atom(JSAtom * atom, Stream * f) {
180 siliconforks 2 assert(ATOM_IS_STRING(atom));
181     JSString * s = ATOM_TO_STRING(atom);
182     print_string(s, f);
183     }
184    
185 siliconforks 174 static void print_regex(jsval value, Stream * f) {
186 siliconforks 2 assert(JSVAL_IS_STRING(value));
187     JSString * s = JSVAL_TO_STRING(value);
188 siliconforks 174 size_t length = JSSTRING_LENGTH(s);
189     jschar * characters = JSSTRING_CHARS(s);
190     for (size_t i = 0; i < length; i++) {
191     jschar c = characters[i];
192     if (32 <= c && c <= 126) {
193     Stream_write_char(f, c);
194     }
195     else {
196     Stream_printf(f, "\\u%04x", c);
197     }
198     }
199 siliconforks 2 }
200    
201 siliconforks 92 static void print_quoted_string_atom(JSAtom * atom, Stream * f) {
202 siliconforks 2 assert(ATOM_IS_STRING(atom));
203     JSString * s = ATOM_TO_STRING(atom);
204 siliconforks 174 Stream_write_char(f, '"');
205     print_string(s, f);
206     Stream_write_char(f, '"');
207 siliconforks 2 }
208    
209     static const char * get_op(uint8 op) {
210     switch(op) {
211 siliconforks 336 case JSOP_OR:
212     return "||";
213     case JSOP_AND:
214     return "&&";
215 siliconforks 2 case JSOP_BITOR:
216     return "|";
217     case JSOP_BITXOR:
218     return "^";
219     case JSOP_BITAND:
220     return "&";
221     case JSOP_EQ:
222     return "==";
223     case JSOP_NE:
224     return "!=";
225 siliconforks 336 case JSOP_STRICTEQ:
226 siliconforks 2 return "===";
227 siliconforks 336 case JSOP_STRICTNE:
228 siliconforks 2 return "!==";
229     case JSOP_LT:
230     return "<";
231     case JSOP_LE:
232     return "<=";
233     case JSOP_GT:
234     return ">";
235     case JSOP_GE:
236     return ">=";
237     case JSOP_LSH:
238     return "<<";
239     case JSOP_RSH:
240     return ">>";
241     case JSOP_URSH:
242     return ">>>";
243     case JSOP_ADD:
244     return "+";
245     case JSOP_SUB:
246     return "-";
247     case JSOP_MUL:
248     return "*";
249     case JSOP_DIV:
250     return "/";
251     case JSOP_MOD:
252     return "%";
253     default:
254     abort();
255     }
256     }
257    
258 siliconforks 92 static void instrument_expression(JSParseNode * node, Stream * f);
259 siliconforks 240 static void instrument_statement(JSParseNode * node, Stream * f, int indent, bool is_jscoverage_if);
260 siliconforks 345 static void output_statement(JSParseNode * node, Stream * f, int indent, bool is_jscoverage_if);
261 siliconforks 2
262 siliconforks 155 enum FunctionType {
263     FUNCTION_NORMAL,
264     FUNCTION_GETTER_OR_SETTER
265     };
266    
267     static void instrument_function(JSParseNode * node, Stream * f, int indent, enum FunctionType type) {
268 siliconforks 336 assert(node->pn_type == TOK_FUNCTION);
269 siliconforks 156 assert(node->pn_arity == PN_FUNC);
270 siliconforks 336 JSObject * object = node->pn_funpob->object;
271 siliconforks 156 assert(JS_ObjectIsFunction(context, object));
272     JSFunction * function = (JSFunction *) JS_GetPrivate(context, object);
273     assert(function);
274 siliconforks 336 assert(object == &function->object);
275 siliconforks 156 Stream_printf(f, "%*s", indent, "");
276     if (type == FUNCTION_NORMAL) {
277     Stream_write_string(f, "function");
278     }
279 siliconforks 2
280 siliconforks 156 /* function name */
281     if (function->atom) {
282     Stream_write_char(f, ' ');
283     print_string_atom(function->atom, f);
284     }
285 siliconforks 2
286 siliconforks 336 /*
287     function parameters - see JS_DecompileFunction in jsapi.cpp, which calls
288     js_DecompileFunction in jsopcode.cpp
289     */
290 siliconforks 156 Stream_write_string(f, "(");
291 siliconforks 336 JSArenaPool pool;
292     JS_INIT_ARENA_POOL(&pool, "instrument_function", 256, 1, &context->scriptStackQuota);
293     jsuword * local_names = NULL;
294     if (JS_GET_LOCAL_NAME_COUNT(function)) {
295     local_names = js_GetLocalNameArray(context, function, &pool);
296     if (local_names == NULL) {
297     fatal("out of memory");
298 siliconforks 2 }
299 siliconforks 156 }
300     for (int i = 0; i < function->nargs; i++) {
301     if (i > 0) {
302     Stream_write_string(f, ", ");
303 siliconforks 2 }
304 siliconforks 336 JSAtom * param = JS_LOCAL_NAME_TO_ATOM(local_names[i]);
305     print_string_atom(param, f);
306 siliconforks 156 }
307 siliconforks 336 JS_FinishArenaPool(&pool);
308 siliconforks 156 Stream_write_string(f, ") {\n");
309 siliconforks 2
310 siliconforks 156 /* function body */
311 siliconforks 345 if (function->flags & JSFUN_EXPR_CLOSURE) {
312     /* expression closure */
313     output_statement(node->pn_body, f, indent + 2, false);
314     }
315     else {
316     instrument_statement(node->pn_body, f, indent + 2, false);
317     }
318 siliconforks 2
319 siliconforks 156 Stream_write_string(f, "}\n");
320 siliconforks 2 }
321    
322 siliconforks 92 static void instrument_function_call(JSParseNode * node, Stream * f) {
323 siliconforks 2 instrument_expression(node->pn_head, f);
324 siliconforks 92 Stream_write_char(f, '(');
325 siliconforks 2 for (struct JSParseNode * p = node->pn_head->pn_next; p != NULL; p = p->pn_next) {
326     if (p != node->pn_head->pn_next) {
327 siliconforks 92 Stream_write_string(f, ", ");
328 siliconforks 2 }
329     instrument_expression(p, f);
330     }
331 siliconforks 92 Stream_write_char(f, ')');
332 siliconforks 2 }
333    
334 siliconforks 340 static void instrument_declarations(JSParseNode * list, Stream * f) {
335     assert(list->pn_arity == PN_LIST);
336     for (JSParseNode * p = list->pn_head; p != NULL; p = p->pn_next) {
337 siliconforks 341 switch (p->pn_type) {
338     case TOK_NAME:
339     assert(p->pn_arity == PN_NAME);
340     if (p != list->pn_head) {
341     Stream_write_string(f, ", ");
342     }
343     print_string_atom(p->pn_atom, f);
344     if (p->pn_expr != NULL) {
345     Stream_write_string(f, " = ");
346     instrument_expression(p->pn_expr, f);
347     }
348     break;
349     case TOK_ASSIGN:
350     case TOK_RB:
351     case TOK_RC:
352     /* destructuring */
353     instrument_expression(p, f);
354     break;
355     default:
356     abort();
357     break;
358 siliconforks 340 }
359     }
360     }
361    
362 siliconforks 343 static void output_for_in(JSParseNode * node, Stream * f) {
363     assert(node->pn_type == TOK_FOR);
364     assert(node->pn_arity == PN_BINARY);
365     Stream_write_string(f, "for ");
366     if (node->pn_iflags & JSITER_FOREACH) {
367     Stream_write_string(f, "each ");
368     }
369     Stream_write_char(f, '(');
370     instrument_expression(node->pn_left, f);
371     Stream_write_char(f, ')');
372     }
373    
374 siliconforks 2 /*
375     See <Expressions> in jsparse.h.
376     TOK_FUNCTION is handled as a statement and as an expression.
377     TOK_DBLDOT is not handled (XML op).
378     TOK_DEFSHARP and TOK_USESHARP are not handled.
379     TOK_ANYNAME is not handled (XML op).
380     TOK_AT is not handled (XML op).
381     TOK_DBLCOLON is not handled.
382     TOK_XML* are not handled.
383     There seem to be some undocumented expressions:
384     TOK_INSTANCEOF binary
385     TOK_IN binary
386     */
387 siliconforks 92 static void instrument_expression(JSParseNode * node, Stream * f) {
388 siliconforks 2 switch (node->pn_type) {
389     case TOK_FUNCTION:
390 siliconforks 155 instrument_function(node, f, 0, FUNCTION_NORMAL);
391 siliconforks 2 break;
392     case TOK_COMMA:
393     for (struct JSParseNode * p = node->pn_head; p != NULL; p = p->pn_next) {
394     if (p != node->pn_head) {
395 siliconforks 92 Stream_write_string(f, ", ");
396 siliconforks 2 }
397     instrument_expression(p, f);
398     }
399     break;
400     case TOK_ASSIGN:
401     instrument_expression(node->pn_left, f);
402 siliconforks 92 Stream_write_char(f, ' ');
403 siliconforks 2 switch (node->pn_op) {
404     case JSOP_ADD:
405     case JSOP_SUB:
406     case JSOP_MUL:
407     case JSOP_MOD:
408     case JSOP_LSH:
409     case JSOP_RSH:
410     case JSOP_URSH:
411     case JSOP_BITAND:
412     case JSOP_BITOR:
413     case JSOP_BITXOR:
414     case JSOP_DIV:
415 siliconforks 92 Stream_printf(f, "%s", get_op(node->pn_op));
416 siliconforks 2 break;
417     default:
418     /* do nothing - it must be a simple assignment */
419     break;
420     }
421 siliconforks 92 Stream_write_string(f, "= ");
422 siliconforks 2 instrument_expression(node->pn_right, f);
423     break;
424     case TOK_HOOK:
425     instrument_expression(node->pn_kid1, f);
426 siliconforks 92 Stream_write_string(f, "? ");
427 siliconforks 2 instrument_expression(node->pn_kid2, f);
428 siliconforks 92 Stream_write_string(f, ": ");
429 siliconforks 2 instrument_expression(node->pn_kid3, f);
430     break;
431     case TOK_OR:
432     case TOK_AND:
433     case TOK_BITOR:
434     case TOK_BITXOR:
435     case TOK_BITAND:
436     case TOK_EQOP:
437     case TOK_RELOP:
438     case TOK_SHOP:
439     case TOK_PLUS:
440     case TOK_MINUS:
441     case TOK_STAR:
442     case TOK_DIVOP:
443     switch (node->pn_arity) {
444     case PN_BINARY:
445     instrument_expression(node->pn_left, f);
446 siliconforks 92 Stream_printf(f, " %s ", get_op(node->pn_op));
447 siliconforks 2 instrument_expression(node->pn_right, f);
448     break;
449     case PN_LIST:
450     for (struct JSParseNode * p = node->pn_head; p != NULL; p = p->pn_next) {
451     if (p != node->pn_head) {
452 siliconforks 92 Stream_printf(f, " %s ", get_op(node->pn_op));
453 siliconforks 2 }
454     instrument_expression(p, f);
455     }
456     break;
457     default:
458     abort();
459     }
460     break;
461     case TOK_UNARYOP:
462     switch (node->pn_op) {
463     case JSOP_NEG:
464 siliconforks 92 Stream_write_char(f, '-');
465 siliconforks 2 instrument_expression(node->pn_kid, f);
466     break;
467     case JSOP_POS:
468 siliconforks 92 Stream_write_char(f, '+');
469 siliconforks 2 instrument_expression(node->pn_kid, f);
470     break;
471     case JSOP_NOT:
472 siliconforks 92 Stream_write_char(f, '!');
473 siliconforks 2 instrument_expression(node->pn_kid, f);
474     break;
475     case JSOP_BITNOT:
476 siliconforks 92 Stream_write_char(f, '~');
477 siliconforks 2 instrument_expression(node->pn_kid, f);
478     break;
479     case JSOP_TYPEOF:
480 siliconforks 92 Stream_write_string(f, "typeof ");
481 siliconforks 2 instrument_expression(node->pn_kid, f);
482     break;
483     case JSOP_VOID:
484 siliconforks 92 Stream_write_string(f, "void ");
485 siliconforks 2 instrument_expression(node->pn_kid, f);
486     break;
487     default:
488     abort();
489     break;
490     }
491     break;
492     case TOK_INC:
493     case TOK_DEC:
494     /*
495     This is not documented, but node->pn_op tells whether it is pre- or post-increment.
496     */
497     switch (node->pn_op) {
498     case JSOP_INCNAME:
499     case JSOP_INCPROP:
500     case JSOP_INCELEM:
501 siliconforks 92 Stream_write_string(f, "++");
502 siliconforks 2 instrument_expression(node->pn_kid, f);
503     break;
504     case JSOP_DECNAME:
505     case JSOP_DECPROP:
506     case JSOP_DECELEM:
507 siliconforks 92 Stream_write_string(f, "--");
508 siliconforks 2 instrument_expression(node->pn_kid, f);
509     break;
510     case JSOP_NAMEINC:
511     case JSOP_PROPINC:
512     case JSOP_ELEMINC:
513     instrument_expression(node->pn_kid, f);
514 siliconforks 92 Stream_write_string(f, "++");
515 siliconforks 2 break;
516     case JSOP_NAMEDEC:
517     case JSOP_PROPDEC:
518     case JSOP_ELEMDEC:
519     instrument_expression(node->pn_kid, f);
520 siliconforks 92 Stream_write_string(f, "--");
521 siliconforks 2 break;
522     default:
523     abort();
524     break;
525     }
526     break;
527     case TOK_NEW:
528 siliconforks 92 Stream_write_string(f, "new ");
529 siliconforks 2 instrument_function_call(node, f);
530     break;
531     case TOK_DELETE:
532 siliconforks 92 Stream_write_string(f, "delete ");
533 siliconforks 2 instrument_expression(node->pn_kid, f);
534     break;
535     case TOK_DOT:
536     /*
537     This may have originally been x['foo-bar']. Because the string 'foo-bar'
538     contains illegal characters, we have to use the subscript syntax instead of
539     the dot syntax.
540     */
541     instrument_expression(node->pn_expr, f);
542 siliconforks 84 assert(ATOM_IS_STRING(node->pn_atom));
543     {
544     JSString * s = ATOM_TO_STRING(node->pn_atom);
545 siliconforks 336 bool is_keyword = (js_CheckKeyword(JSSTRING_CHARS(s), JSSTRING_LENGTH(s)) != TOK_EOF);
546     if (! is_keyword && js_IsIdentifier(s)) {
547 siliconforks 92 Stream_write_char(f, '.');
548 siliconforks 84 print_string_atom(node->pn_atom, f);
549     }
550     else {
551 siliconforks 92 Stream_write_char(f, '[');
552 siliconforks 84 print_quoted_string_atom(node->pn_atom, f);
553 siliconforks 92 Stream_write_char(f, ']');
554 siliconforks 84 }
555     }
556 siliconforks 2 break;
557     case TOK_LB:
558     instrument_expression(node->pn_left, f);
559 siliconforks 92 Stream_write_char(f, '[');
560 siliconforks 2 instrument_expression(node->pn_right, f);
561 siliconforks 92 Stream_write_char(f, ']');
562 siliconforks 2 break;
563     case TOK_LP:
564     instrument_function_call(node, f);
565     break;
566     case TOK_RB:
567 siliconforks 92 Stream_write_char(f, '[');
568 siliconforks 2 for (struct JSParseNode * p = node->pn_head; p != NULL; p = p->pn_next) {
569     if (p != node->pn_head) {
570 siliconforks 92 Stream_write_string(f, ", ");
571 siliconforks 2 }
572     /* TOK_COMMA is a special case: a hole in the array */
573     if (p->pn_type != TOK_COMMA) {
574     instrument_expression(p, f);
575     }
576     }
577     if (node->pn_extra == PNX_ENDCOMMA) {
578 siliconforks 92 Stream_write_char(f, ',');
579 siliconforks 2 }
580 siliconforks 92 Stream_write_char(f, ']');
581 siliconforks 2 break;
582     case TOK_RC:
583 siliconforks 92 Stream_write_char(f, '{');
584 siliconforks 2 for (struct JSParseNode * p = node->pn_head; p != NULL; p = p->pn_next) {
585     assert(p->pn_type == TOK_COLON);
586     if (p != node->pn_head) {
587 siliconforks 92 Stream_write_string(f, ", ");
588 siliconforks 2 }
589 siliconforks 155
590     /* check whether this is a getter or setter */
591     switch (p->pn_op) {
592     case JSOP_GETTER:
593     case JSOP_SETTER:
594 siliconforks 293 if (p->pn_op == JSOP_GETTER) {
595     Stream_write_string(f, "get ");
596     }
597     else {
598     Stream_write_string(f, "set ");
599     }
600 siliconforks 155 instrument_expression(p->pn_left, f);
601 siliconforks 293 if (p->pn_right->pn_type != TOK_FUNCTION) {
602     fatal("parse error: expected function");
603     }
604 siliconforks 155 instrument_function(p->pn_right, f, 0, FUNCTION_GETTER_OR_SETTER);
605     break;
606     default:
607     instrument_expression(p->pn_left, f);
608     Stream_write_string(f, ": ");
609     instrument_expression(p->pn_right, f);
610     break;
611     }
612 siliconforks 2 }
613 siliconforks 92 Stream_write_char(f, '}');
614 siliconforks 2 break;
615     case TOK_RP:
616 siliconforks 92 Stream_write_char(f, '(');
617 siliconforks 2 instrument_expression(node->pn_kid, f);
618 siliconforks 92 Stream_write_char(f, ')');
619 siliconforks 2 break;
620     case TOK_NAME:
621     print_string_atom(node->pn_atom, f);
622     break;
623     case TOK_STRING:
624     print_quoted_string_atom(node->pn_atom, f);
625     break;
626 siliconforks 336 case TOK_REGEXP:
627     assert(node->pn_op == JSOP_REGEXP);
628     {
629     JSObject * object = node->pn_pob->object;
630     jsval result;
631     js_regexp_toString(context, object, &result);
632     print_regex(result, f);
633 siliconforks 2 }
634     break;
635     case TOK_NUMBER:
636     /*
637     A 64-bit IEEE 754 floating point number has a 52-bit fraction.
638     2^(-52) = 2.22 x 10^(-16)
639     Thus there are 16 significant digits.
640     To keep the output simple, special-case zero.
641     */
642     if (node->pn_dval == 0.0) {
643 siliconforks 92 Stream_write_string(f, "0");
644 siliconforks 2 }
645     else {
646 siliconforks 92 Stream_printf(f, "%.15g", node->pn_dval);
647 siliconforks 2 }
648     break;
649     case TOK_PRIMARY:
650     switch (node->pn_op) {
651     case JSOP_TRUE:
652 siliconforks 92 Stream_write_string(f, "true");
653 siliconforks 2 break;
654     case JSOP_FALSE:
655 siliconforks 92 Stream_write_string(f, "false");
656 siliconforks 2 break;
657     case JSOP_NULL:
658 siliconforks 92 Stream_write_string(f, "null");
659 siliconforks 2 break;
660     case JSOP_THIS:
661 siliconforks 92 Stream_write_string(f, "this");
662 siliconforks 2 break;
663     /* jsscan.h mentions `super' ??? */
664     default:
665     abort();
666     }
667     break;
668     case TOK_INSTANCEOF:
669     instrument_expression(node->pn_left, f);
670 siliconforks 92 Stream_write_string(f, " instanceof ");
671 siliconforks 2 instrument_expression(node->pn_right, f);
672     break;
673     case TOK_IN:
674     instrument_expression(node->pn_left, f);
675 siliconforks 92 Stream_write_string(f, " in ");
676 siliconforks 2 instrument_expression(node->pn_right, f);
677     break;
678 siliconforks 340 case TOK_LEXICALSCOPE:
679     assert(node->pn_arity == PN_NAME);
680     assert(node->pn_expr->pn_type == TOK_LET);
681     assert(node->pn_expr->pn_arity == PN_BINARY);
682     Stream_write_string(f, "let(");
683     assert(node->pn_expr->pn_left->pn_type == TOK_LP);
684     assert(node->pn_expr->pn_left->pn_arity == PN_LIST);
685     instrument_declarations(node->pn_expr->pn_left, f);
686     Stream_write_string(f, ") ");
687     instrument_expression(node->pn_expr->pn_right, f);
688     break;
689 siliconforks 342 case TOK_YIELD:
690     assert(node->pn_arity == PN_UNARY);
691     Stream_write_string(f, "yield ");
692     instrument_expression(node->pn_kid, f);
693     break;
694 siliconforks 343 case TOK_ARRAYCOMP:
695     assert(node->pn_arity == PN_LIST);
696     {
697     JSParseNode * block_node;
698     switch (node->pn_count) {
699     case 1:
700     block_node = node->pn_head;
701     break;
702     case 2:
703     block_node = node->pn_head->pn_next;
704     break;
705     default:
706     abort();
707     break;
708     }
709     assert(block_node->pn_type == TOK_LEXICALSCOPE);
710     assert(block_node->pn_arity == PN_NAME);
711     JSParseNode * for_node = block_node->pn_expr;
712     assert(for_node->pn_type == TOK_FOR);
713     assert(for_node->pn_arity == PN_BINARY);
714     JSParseNode * push_node;
715     JSParseNode * if_node = NULL;
716     switch (for_node->pn_right->pn_type) {
717     case TOK_ARRAYPUSH:
718     push_node = for_node->pn_right;
719     assert(push_node->pn_arity == PN_UNARY);
720     break;
721     case TOK_IF:
722     if_node = for_node->pn_right;
723     assert(if_node->pn_arity == PN_TERNARY);
724     push_node = if_node->pn_kid2;
725     break;
726     default:
727     abort();
728     break;
729     }
730     Stream_write_char(f, '[');
731     instrument_expression(push_node->pn_kid, f);
732     Stream_write_char(f, ' ');
733     output_for_in(for_node, f);
734     if (if_node) {
735     Stream_write_string(f, " if (");
736     instrument_expression(if_node->pn_kid1, f);
737     Stream_write_char(f, ')');
738     }
739     Stream_write_char(f, ']');
740     }
741     break;
742     case TOK_VAR:
743     assert(node->pn_arity == PN_LIST);
744     Stream_write_string(f, "var ");
745     instrument_declarations(node, f);
746     break;
747     case TOK_LET:
748     assert(node->pn_arity == PN_LIST);
749     Stream_write_string(f, "let ");
750     instrument_declarations(node, f);
751     break;
752 siliconforks 2 default:
753     fatal("unsupported node type in file %s: %d", file_id, node->pn_type);
754     }
755     }
756    
757 siliconforks 240 static void output_statement(JSParseNode * node, Stream * f, int indent, bool is_jscoverage_if) {
758 siliconforks 2 switch (node->pn_type) {
759     case TOK_FUNCTION:
760 siliconforks 155 instrument_function(node, f, indent, FUNCTION_NORMAL);
761 siliconforks 2 break;
762     case TOK_LC:
763     assert(node->pn_arity == PN_LIST);
764     /*
765 siliconforks 92 Stream_write_string(f, "{\n");
766 siliconforks 2 */
767     for (struct JSParseNode * p = node->pn_u.list.head; p != NULL; p = p->pn_next) {
768 siliconforks 240 instrument_statement(p, f, indent, false);
769 siliconforks 2 }
770     /*
771 siliconforks 92 Stream_printf(f, "%*s", indent, "");
772     Stream_write_string(f, "}\n");
773 siliconforks 2 */
774     break;
775     case TOK_IF:
776 siliconforks 240 {
777 siliconforks 2 assert(node->pn_arity == PN_TERNARY);
778 siliconforks 240
779     uint16_t line = node->pn_pos.begin.lineno;
780     if (! is_jscoverage_if) {
781     if (line > num_lines) {
782     fatal("%s: script contains more than 65,535 lines", file_id);
783     }
784     if (line >= 2 && exclusive_directives[line - 2]) {
785     is_jscoverage_if = true;
786     }
787     }
788    
789 siliconforks 92 Stream_printf(f, "%*s", indent, "");
790     Stream_write_string(f, "if (");
791 siliconforks 2 instrument_expression(node->pn_kid1, f);
792 siliconforks 92 Stream_write_string(f, ") {\n");
793 siliconforks 240 if (is_jscoverage_if && node->pn_kid3) {
794     uint16_t else_start = node->pn_kid3->pn_pos.begin.lineno;
795     uint16_t else_end = node->pn_kid3->pn_pos.end.lineno + 1;
796     Stream_printf(f, "%*s", indent + 2, "");
797     Stream_printf(f, "_$jscoverage['%s'].conditionals[%d] = %d;\n", file_id, else_start, else_end);
798     }
799     instrument_statement(node->pn_kid2, f, indent + 2, false);
800 siliconforks 92 Stream_printf(f, "%*s", indent, "");
801     Stream_write_string(f, "}\n");
802 siliconforks 240
803     if (node->pn_kid3 || is_jscoverage_if) {
804 siliconforks 92 Stream_printf(f, "%*s", indent, "");
805     Stream_write_string(f, "else {\n");
806 siliconforks 240
807     if (is_jscoverage_if) {
808     uint16_t if_start = node->pn_kid2->pn_pos.begin.lineno + 1;
809     uint16_t if_end = node->pn_kid2->pn_pos.end.lineno + 1;
810     Stream_printf(f, "%*s", indent + 2, "");
811     Stream_printf(f, "_$jscoverage['%s'].conditionals[%d] = %d;\n", file_id, if_start, if_end);
812     }
813    
814     if (node->pn_kid3) {
815     instrument_statement(node->pn_kid3, f, indent + 2, is_jscoverage_if);
816     }
817    
818 siliconforks 92 Stream_printf(f, "%*s", indent, "");
819     Stream_write_string(f, "}\n");
820 siliconforks 2 }
821 siliconforks 240
822 siliconforks 2 break;
823 siliconforks 240 }
824 siliconforks 2 case TOK_SWITCH:
825     assert(node->pn_arity == PN_BINARY);
826 siliconforks 92 Stream_printf(f, "%*s", indent, "");
827     Stream_write_string(f, "switch (");
828 siliconforks 2 instrument_expression(node->pn_left, f);
829 siliconforks 92 Stream_write_string(f, ") {\n");
830 siliconforks 2 for (struct JSParseNode * p = node->pn_right->pn_head; p != NULL; p = p->pn_next) {
831 siliconforks 92 Stream_printf(f, "%*s", indent, "");
832 siliconforks 2 switch (p->pn_type) {
833     case TOK_CASE:
834 siliconforks 92 Stream_write_string(f, "case ");
835 siliconforks 2 instrument_expression(p->pn_left, f);
836 siliconforks 92 Stream_write_string(f, ":\n");
837 siliconforks 2 break;
838     case TOK_DEFAULT:
839 siliconforks 92 Stream_write_string(f, "default:\n");
840 siliconforks 2 break;
841     default:
842     abort();
843     break;
844     }
845 siliconforks 240 instrument_statement(p->pn_right, f, indent + 2, false);
846 siliconforks 2 }
847 siliconforks 92 Stream_printf(f, "%*s", indent, "");
848     Stream_write_string(f, "}\n");
849 siliconforks 2 break;
850     case TOK_CASE:
851     case TOK_DEFAULT:
852     abort();
853     break;
854     case TOK_WHILE:
855     assert(node->pn_arity == PN_BINARY);
856 siliconforks 92 Stream_printf(f, "%*s", indent, "");
857     Stream_write_string(f, "while (");
858 siliconforks 2 instrument_expression(node->pn_left, f);
859 siliconforks 92 Stream_write_string(f, ") {\n");
860 siliconforks 240 instrument_statement(node->pn_right, f, indent + 2, false);
861 siliconforks 92 Stream_write_string(f, "}\n");
862 siliconforks 2 break;
863     case TOK_DO:
864     assert(node->pn_arity == PN_BINARY);
865 siliconforks 92 Stream_printf(f, "%*s", indent, "");
866     Stream_write_string(f, "do {\n");
867 siliconforks 240 instrument_statement(node->pn_left, f, indent + 2, false);
868 siliconforks 92 Stream_write_string(f, "}\n");
869     Stream_printf(f, "%*s", indent, "");
870     Stream_write_string(f, "while (");
871 siliconforks 2 instrument_expression(node->pn_right, f);
872 siliconforks 92 Stream_write_string(f, ");\n");
873 siliconforks 2 break;
874     case TOK_FOR:
875     assert(node->pn_arity == PN_BINARY);
876 siliconforks 92 Stream_printf(f, "%*s", indent, "");
877 siliconforks 2 switch (node->pn_left->pn_type) {
878     case TOK_IN:
879     /* for/in */
880     assert(node->pn_left->pn_arity == PN_BINARY);
881 siliconforks 343 output_for_in(node, f);
882 siliconforks 2 break;
883     case TOK_RESERVED:
884     /* for (;;) */
885     assert(node->pn_left->pn_arity == PN_TERNARY);
886 siliconforks 343 Stream_write_string(f, "for (");
887 siliconforks 2 if (node->pn_left->pn_kid1) {
888 siliconforks 343 instrument_expression(node->pn_left->pn_kid1, f);
889 siliconforks 2 }
890 siliconforks 92 Stream_write_string(f, ";");
891 siliconforks 2 if (node->pn_left->pn_kid2) {
892 siliconforks 92 Stream_write_char(f, ' ');
893 siliconforks 2 instrument_expression(node->pn_left->pn_kid2, f);
894     }
895 siliconforks 92 Stream_write_string(f, ";");
896 siliconforks 2 if (node->pn_left->pn_kid3) {
897 siliconforks 92 Stream_write_char(f, ' ');
898 siliconforks 2 instrument_expression(node->pn_left->pn_kid3, f);
899     }
900 siliconforks 343 Stream_write_char(f, ')');
901 siliconforks 2 break;
902     default:
903     abort();
904     break;
905     }
906 siliconforks 343 Stream_write_string(f, " {\n");
907 siliconforks 240 instrument_statement(node->pn_right, f, indent + 2, false);
908 siliconforks 92 Stream_write_string(f, "}\n");
909 siliconforks 2 break;
910     case TOK_THROW:
911     assert(node->pn_arity == PN_UNARY);
912 siliconforks 92 Stream_printf(f, "%*s", indent, "");
913     Stream_write_string(f, "throw ");
914 siliconforks 2 instrument_expression(node->pn_u.unary.kid, f);
915 siliconforks 92 Stream_write_string(f, ";\n");
916 siliconforks 2 break;
917     case TOK_TRY:
918 siliconforks 92 Stream_printf(f, "%*s", indent, "");
919     Stream_write_string(f, "try {\n");
920 siliconforks 240 instrument_statement(node->pn_kid1, f, indent + 2, false);
921 siliconforks 92 Stream_printf(f, "%*s", indent, "");
922     Stream_write_string(f, "}\n");
923 siliconforks 336 if (node->pn_kid2) {
924     assert(node->pn_kid2->pn_type == TOK_RESERVED);
925     for (JSParseNode * scope = node->pn_kid2->pn_head; scope != NULL; scope = scope->pn_next) {
926     assert(scope->pn_type == TOK_LEXICALSCOPE);
927     JSParseNode * catch = scope->pn_expr;
928 siliconforks 2 assert(catch->pn_type == TOK_CATCH);
929 siliconforks 92 Stream_printf(f, "%*s", indent, "");
930     Stream_write_string(f, "catch (");
931 siliconforks 341 /* this may not be a name - destructuring assignment */
932     /*
933 siliconforks 2 assert(catch->pn_kid1->pn_arity == PN_NAME);
934     print_string_atom(catch->pn_kid1->pn_atom, f);
935 siliconforks 341 */
936     instrument_expression(catch->pn_kid1, f);
937 siliconforks 336 if (catch->pn_kid2) {
938 siliconforks 92 Stream_write_string(f, " if ");
939 siliconforks 336 instrument_expression(catch->pn_kid2, f);
940 siliconforks 2 }
941 siliconforks 92 Stream_write_string(f, ") {\n");
942 siliconforks 240 instrument_statement(catch->pn_kid3, f, indent + 2, false);
943 siliconforks 92 Stream_printf(f, "%*s", indent, "");
944     Stream_write_string(f, "}\n");
945 siliconforks 2 }
946     }
947     if (node->pn_kid3) {
948 siliconforks 92 Stream_printf(f, "%*s", indent, "");
949     Stream_write_string(f, "finally {\n");
950 siliconforks 240 instrument_statement(node->pn_kid3, f, indent + 2, false);
951 siliconforks 92 Stream_printf(f, "%*s", indent, "");
952     Stream_write_string(f, "}\n");
953 siliconforks 2 }
954     break;
955     case TOK_CATCH:
956     abort();
957     break;
958     case TOK_BREAK:
959     case TOK_CONTINUE:
960     assert(node->pn_arity == PN_NAME || node->pn_arity == PN_NULLARY);
961 siliconforks 92 Stream_printf(f, "%*s", indent, "");
962     Stream_write_string(f, node->pn_type == TOK_BREAK? "break": "continue");
963 siliconforks 2 JSAtom * atom = node->pn_u.name.atom;
964     if (atom != NULL) {
965 siliconforks 92 Stream_write_char(f, ' ');
966 siliconforks 2 print_string_atom(node->pn_atom, f);
967     }
968 siliconforks 92 Stream_write_string(f, ";\n");
969 siliconforks 2 break;
970     case TOK_WITH:
971     assert(node->pn_arity == PN_BINARY);
972 siliconforks 92 Stream_printf(f, "%*s", indent, "");
973     Stream_write_string(f, "with (");
974 siliconforks 2 instrument_expression(node->pn_left, f);
975 siliconforks 92 Stream_write_string(f, ") {\n");
976 siliconforks 240 instrument_statement(node->pn_right, f, indent + 2, false);
977 siliconforks 92 Stream_printf(f, "%*s", indent, "");
978     Stream_write_string(f, "}\n");
979 siliconforks 2 break;
980     case TOK_VAR:
981 siliconforks 343 Stream_printf(f, "%*s", indent, "");
982     instrument_expression(node, f);
983 siliconforks 92 Stream_write_string(f, ";\n");
984 siliconforks 2 break;
985     case TOK_RETURN:
986     assert(node->pn_arity == PN_UNARY);
987 siliconforks 92 Stream_printf(f, "%*s", indent, "");
988     Stream_write_string(f, "return");
989 siliconforks 2 if (node->pn_kid != NULL) {
990 siliconforks 92 Stream_write_char(f, ' ');
991 siliconforks 2 instrument_expression(node->pn_kid, f);
992     }
993 siliconforks 92 Stream_write_string(f, ";\n");
994 siliconforks 2 break;
995     case TOK_SEMI:
996     assert(node->pn_arity == PN_UNARY);
997 siliconforks 92 Stream_printf(f, "%*s", indent, "");
998 siliconforks 2 if (node->pn_kid != NULL) {
999     instrument_expression(node->pn_kid, f);
1000     }
1001 siliconforks 92 Stream_write_string(f, ";\n");
1002 siliconforks 2 break;
1003     case TOK_COLON:
1004     assert(node->pn_arity == PN_NAME);
1005     /*
1006     This one is tricky: can't output instrumentation between the label and the
1007     statement it's supposed to label ...
1008     */
1009 siliconforks 92 Stream_printf(f, "%*s", indent < 2? 0: indent - 2, "");
1010 siliconforks 2 print_string_atom(node->pn_atom, f);
1011 siliconforks 92 Stream_write_string(f, ":\n");
1012 siliconforks 2 /*
1013     ... use output_statement instead of instrument_statement.
1014     */
1015 siliconforks 240 output_statement(node->pn_expr, f, indent, false);
1016 siliconforks 2 break;
1017 siliconforks 340 case TOK_LEXICALSCOPE:
1018     /* let statement */
1019     assert(node->pn_arity == PN_NAME);
1020     switch (node->pn_expr->pn_type) {
1021     case TOK_LET:
1022     /* let statement */
1023     assert(node->pn_expr->pn_arity == PN_BINARY);
1024     instrument_statement(node->pn_expr, f, indent, false);
1025     break;
1026     case TOK_LC:
1027     /* block */
1028     Stream_printf(f, "%*s", indent, "");
1029     Stream_write_string(f, "{\n");
1030     instrument_statement(node->pn_expr, f, indent + 2, false);
1031     Stream_printf(f, "%*s", indent, "");
1032     Stream_write_string(f, "}\n");
1033     break;
1034     case TOK_FOR:
1035     instrument_statement(node->pn_expr, f, indent, false);
1036     break;
1037     default:
1038     abort();
1039     break;
1040     }
1041     break;
1042     case TOK_LET:
1043     switch (node->pn_arity) {
1044     case PN_BINARY:
1045     /* let statement */
1046     Stream_printf(f, "%*s", indent, "");
1047     Stream_write_string(f, "let (");
1048     assert(node->pn_left->pn_type == TOK_LP);
1049     assert(node->pn_left->pn_arity == PN_LIST);
1050     instrument_declarations(node->pn_left, f);
1051     Stream_write_string(f, ") {\n");
1052     instrument_statement(node->pn_right, f, indent + 2, false);
1053     Stream_printf(f, "%*s", indent, "");
1054     Stream_write_string(f, "}\n");
1055     break;
1056     case PN_LIST:
1057     /* let definition */
1058 siliconforks 343 Stream_printf(f, "%*s", indent, "");
1059     instrument_expression(node, f);
1060 siliconforks 340 Stream_write_string(f, ";\n");
1061     break;
1062     default:
1063     abort();
1064     break;
1065     }
1066     break;
1067 siliconforks 2 default:
1068     fatal("unsupported node type in file %s: %d", file_id, node->pn_type);
1069     }
1070     }
1071    
1072     /*
1073     See <Statements> in jsparse.h.
1074     TOK_FUNCTION is handled as a statement and as an expression.
1075     TOK_EXPORT, TOK_IMPORT are not handled.
1076     */
1077 siliconforks 240 static void instrument_statement(JSParseNode * node, Stream * f, int indent, bool is_jscoverage_if) {
1078 siliconforks 340 if (node->pn_type != TOK_LC && node->pn_type != TOK_LEXICALSCOPE) {
1079 siliconforks 214 uint16_t line = node->pn_pos.begin.lineno;
1080     if (line > num_lines) {
1081     fatal("%s: script contains more than 65,535 lines", file_id);
1082     }
1083    
1084 siliconforks 2 /* the root node has line number 0 */
1085     if (line != 0) {
1086 siliconforks 92 Stream_printf(f, "%*s", indent, "");
1087     Stream_printf(f, "_$jscoverage['%s'][%d]++;\n", file_id, line);
1088 siliconforks 2 lines[line - 1] = 1;
1089     }
1090     }
1091 siliconforks 240 output_statement(node, f, indent, is_jscoverage_if);
1092 siliconforks 2 }
1093    
1094 siliconforks 190 static bool characters_start_with(const jschar * characters, size_t line_start, size_t line_end, const char * prefix) {
1095     const jschar * characters_end = characters + line_end;
1096     const jschar * cp = characters + line_start;
1097     const char * bp = prefix;
1098     for (;;) {
1099     if (*bp == '\0') {
1100     return true;
1101     }
1102     else if (cp == characters_end) {
1103     return false;
1104     }
1105     else if (*cp != *bp) {
1106     return false;
1107     }
1108     bp++;
1109     cp++;
1110     }
1111     }
1112    
1113 siliconforks 240 static bool characters_are_white_space(const jschar * characters, size_t line_start, size_t line_end) {
1114     /* XXX - other Unicode space */
1115     const jschar * end = characters + line_end;
1116     for (const jschar * p = characters + line_start; p < end; p++) {
1117     jschar c = *p;
1118     if (c == 0x9 || c == 0xB || c == 0xC || c == 0x20 || c == 0xA0) {
1119     continue;
1120     }
1121     else {
1122     return false;
1123     }
1124     }
1125     return true;
1126     }
1127    
1128 siliconforks 284 static void error_reporter(JSContext * context, const char * message, JSErrorReport * report) {
1129     fprintf(stderr, "jscoverage: parse error: line %u: %s\n", report->lineno, message);
1130     }
1131    
1132 siliconforks 179 void jscoverage_instrument_js(const char * id, const uint16_t * characters, size_t num_characters, Stream * output) {
1133 siliconforks 2 file_id = id;
1134    
1135 siliconforks 336 /* parse the javascript */
1136     JSParseContext parse_context;
1137     if (! js_InitParseContext(context, &parse_context, NULL, NULL, characters, num_characters, NULL, NULL, 1)) {
1138 siliconforks 2 fatal("cannot create token stream from file: %s", file_id);
1139     }
1140 siliconforks 284 JSErrorReporter old_error_reporter = JS_SetErrorReporter(context, error_reporter);
1141 siliconforks 336 JSParseNode * node = js_ParseScript(context, global, &parse_context);
1142 siliconforks 2 if (node == NULL) {
1143 siliconforks 284 js_ReportUncaughtException(context);
1144 siliconforks 2 fatal("parse error in file: %s", file_id);
1145     }
1146 siliconforks 284 JS_SetErrorReporter(context, old_error_reporter);
1147 siliconforks 214 num_lines = node->pn_pos.end.lineno;
1148 siliconforks 2 lines = xmalloc(num_lines);
1149 siliconforks 240 for (unsigned int i = 0; i < num_lines; i++) {
1150 siliconforks 2 lines[i] = 0;
1151     }
1152    
1153 siliconforks 240 /* search code for conditionals */
1154     exclusive_directives = xnew(bool, num_lines);
1155     for (unsigned int i = 0; i < num_lines; i++) {
1156     exclusive_directives[i] = false;
1157 siliconforks 2 }
1158    
1159 siliconforks 157 bool has_conditionals = false;
1160 siliconforks 240 struct IfDirective * if_directives = NULL;
1161 siliconforks 157 size_t line_number = 0;
1162     size_t i = 0;
1163 siliconforks 176 while (i < num_characters) {
1164 siliconforks 240 if (line_number == UINT16_MAX) {
1165     fatal("%s: script has more than 65,535 lines", file_id);
1166     }
1167 siliconforks 157 line_number++;
1168     size_t line_start = i;
1169 siliconforks 190 jschar c;
1170     bool done = false;
1171     while (! done && i < num_characters) {
1172     c = characters[i];
1173     switch (c) {
1174     case '\r':
1175     case '\n':
1176     case 0x2028:
1177     case 0x2029:
1178     done = true;
1179     break;
1180     default:
1181     i++;
1182     }
1183 siliconforks 157 }
1184     size_t line_end = i;
1185 siliconforks 176 if (i < num_characters) {
1186 siliconforks 190 i++;
1187     if (c == '\r' && i < num_characters && characters[i] == '\n') {
1188 siliconforks 157 i++;
1189     }
1190     }
1191 siliconforks 240
1192 siliconforks 190 if (characters_start_with(characters, line_start, line_end, "//#JSCOVERAGE_IF")) {
1193 siliconforks 240 has_conditionals = true;
1194    
1195     if (characters_are_white_space(characters, line_start + 16, line_end)) {
1196     exclusive_directives[line_number - 1] = true;
1197 siliconforks 157 }
1198 siliconforks 240 else {
1199     struct IfDirective * if_directive = xnew(struct IfDirective, 1);
1200     if_directive->condition_start = characters + line_start + 16;
1201     if_directive->condition_end = characters + line_end;
1202     if_directive->start_line = line_number;
1203     if_directive->end_line = 0;
1204     if_directive->next = if_directives;
1205     if_directives = if_directive;
1206 siliconforks 190 }
1207 siliconforks 157 }
1208 siliconforks 190 else if (characters_start_with(characters, line_start, line_end, "//#JSCOVERAGE_ENDIF")) {
1209 siliconforks 240 for (struct IfDirective * p = if_directives; p != NULL; p = p->next) {
1210     if (p->end_line == 0) {
1211     p->end_line = line_number;
1212     break;
1213     }
1214     }
1215 siliconforks 157 }
1216     }
1217    
1218 siliconforks 240 /*
1219     An instrumented JavaScript file has 4 sections:
1220     1. initialization
1221     2. instrumented source code
1222     3. conditionals
1223     4. original source code
1224     */
1225    
1226     Stream * instrumented = Stream_new(0);
1227     instrument_statement(node, instrumented, 0, false);
1228 siliconforks 336 js_FinishParseContext(context, &parse_context);
1229 siliconforks 240
1230     /* write line number info to the output */
1231     Stream_write_string(output, "/* automatically generated by JSCoverage - do not edit */\n");
1232     Stream_write_string(output, "if (! top._$jscoverage) {\n top._$jscoverage = {};\n}\n");
1233     Stream_write_string(output, "var _$jscoverage = top._$jscoverage;\n");
1234     Stream_printf(output, "if (! _$jscoverage['%s']) {\n", file_id);
1235     Stream_printf(output, " _$jscoverage['%s'] = [];\n", file_id);
1236     for (int i = 0; i < num_lines; i++) {
1237     if (lines[i]) {
1238     Stream_printf(output, " _$jscoverage['%s'][%d] = 0;\n", file_id, i + 1);
1239     }
1240     }
1241     Stream_write_string(output, "}\n");
1242     free(lines);
1243     lines = NULL;
1244     free(exclusive_directives);
1245     exclusive_directives = NULL;
1246    
1247     /* conditionals */
1248     if (has_conditionals) {
1249     Stream_printf(output, "_$jscoverage['%s'].conditionals = [];\n", file_id);
1250     }
1251    
1252     /* copy the instrumented source code to the output */
1253     Stream_write(output, instrumented->data, instrumented->length);
1254    
1255     /* conditionals */
1256     for (struct IfDirective * if_directive = if_directives; if_directive != NULL; if_directive = if_directive->next) {
1257     Stream_write_string(output, "if (!(");
1258 siliconforks 246 print_javascript(if_directive->condition_start, if_directive->condition_end - if_directive->condition_start, output);
1259 siliconforks 240 Stream_write_string(output, ")) {\n");
1260     Stream_printf(output, " _$jscoverage['%s'].conditionals[%d] = %d;\n", file_id, if_directive->start_line, if_directive->end_line);
1261     Stream_write_string(output, "}\n");
1262     }
1263    
1264     /* free */
1265     while (if_directives != NULL) {
1266     struct IfDirective * if_directive = if_directives;
1267     if_directives = if_directives->next;
1268     free(if_directive);
1269     }
1270    
1271 siliconforks 95 /* copy the original source to the output */
1272 siliconforks 179 Stream_printf(output, "_$jscoverage['%s'].source = ", file_id);
1273     jscoverage_write_source(id, characters, num_characters, output);
1274     Stream_printf(output, ";\n");
1275 siliconforks 95
1276 siliconforks 179 Stream_delete(instrumented);
1277    
1278     file_id = NULL;
1279     }
1280    
1281     void jscoverage_write_source(const char * id, const jschar * characters, size_t num_characters, Stream * output) {
1282     Stream_write_string(output, "[");
1283     if (jscoverage_highlight) {
1284     Stream * highlighted_stream = Stream_new(num_characters);
1285     jscoverage_highlight_js(context, id, characters, num_characters, highlighted_stream);
1286     size_t i = 0;
1287     while (i < highlighted_stream->length) {
1288     if (i > 0) {
1289     Stream_write_char(output, ',');
1290     }
1291    
1292     Stream_write_char(output, '"');
1293     bool done = false;
1294     while (! done) {
1295     char c = highlighted_stream->data[i];
1296     switch (c) {
1297     case 0x8:
1298     /* backspace */
1299     Stream_write_string(output, "\\b");
1300     break;
1301     case 0x9:
1302     /* horizontal tab */
1303     Stream_write_string(output, "\\t");
1304     break;
1305     case 0xa:
1306     /* line feed (new line) */
1307     done = true;
1308     break;
1309 siliconforks 317 /* IE doesn't support this */
1310     /*
1311 siliconforks 179 case 0xb:
1312     Stream_write_string(output, "\\v");
1313     break;
1314 siliconforks 317 */
1315 siliconforks 179 case 0xc:
1316     /* form feed */
1317     Stream_write_string(output, "\\f");
1318     break;
1319     case 0xd:
1320     /* carriage return */
1321     done = true;
1322     if (i + 1 < highlighted_stream->length && highlighted_stream->data[i + 1] == '\n') {
1323     i++;
1324     }
1325     break;
1326     case '"':
1327     Stream_write_string(output, "\\\"");
1328     break;
1329     case '\\':
1330     Stream_write_string(output, "\\\\");
1331     break;
1332     default:
1333     Stream_write_char(output, c);
1334     break;
1335     }
1336 siliconforks 95 i++;
1337 siliconforks 179 if (i >= highlighted_stream->length) {
1338     done = true;
1339 siliconforks 95 }
1340     }
1341 siliconforks 179 Stream_write_char(output, '"');
1342     }
1343     Stream_delete(highlighted_stream);
1344     }
1345     else {
1346     size_t i = 0;
1347     while (i < num_characters) {
1348     if (i > 0) {
1349     Stream_write_char(output, ',');
1350     }
1351    
1352     Stream_write_char(output, '"');
1353     bool done = false;
1354     while (! done) {
1355     jschar c = characters[i];
1356     switch (c) {
1357     case 0x8:
1358     /* backspace */
1359     Stream_write_string(output, "\\b");
1360     break;
1361     case 0x9:
1362     /* horizontal tab */
1363     Stream_write_string(output, "\\t");
1364     break;
1365     case 0xa:
1366     /* line feed (new line) */
1367     done = true;
1368     break;
1369 siliconforks 317 /* IE doesn't support this */
1370     /*
1371 siliconforks 179 case 0xb:
1372     Stream_write_string(output, "\\v");
1373     break;
1374 siliconforks 317 */
1375 siliconforks 179 case 0xc:
1376     /* form feed */
1377     Stream_write_string(output, "\\f");
1378     break;
1379     case 0xd:
1380     /* carriage return */
1381     done = true;
1382     if (i + 1 < num_characters && characters[i + 1] == '\n') {
1383     i++;
1384     }
1385     break;
1386     case '"':
1387     Stream_write_string(output, "\\\"");
1388     break;
1389     case '\\':
1390     Stream_write_string(output, "\\\\");
1391     break;
1392     case '&':
1393     Stream_write_string(output, "&amp;");
1394     break;
1395     case '<':
1396     Stream_write_string(output, "&lt;");
1397     break;
1398     case '>':
1399     Stream_write_string(output, "&gt;");
1400     break;
1401     case 0x2028:
1402     case 0x2029:
1403     done = true;
1404     break;
1405     default:
1406     if (32 <= c && c <= 126) {
1407     Stream_write_char(output, c);
1408     }
1409     else {
1410     Stream_printf(output, "&#%d;", c);
1411     }
1412     break;
1413     }
1414 siliconforks 95 i++;
1415 siliconforks 179 if (i >= num_characters) {
1416     done = true;
1417     }
1418 siliconforks 95 }
1419 siliconforks 179 Stream_write_char(output, '"');
1420 siliconforks 95 }
1421     }
1422 siliconforks 179 Stream_write_string(output, "]");
1423 siliconforks 2 }
1424 siliconforks 116
1425     void jscoverage_copy_resources(const char * destination_directory) {
1426     copy_resource("jscoverage.html", destination_directory);
1427     copy_resource("jscoverage.css", destination_directory);
1428     copy_resource("jscoverage.js", destination_directory);
1429 siliconforks 169 copy_resource("jscoverage-ie.css", destination_directory);
1430 siliconforks 116 copy_resource("jscoverage-throbber.gif", destination_directory);
1431 siliconforks 179 copy_resource("jscoverage-highlight.css", destination_directory);
1432 siliconforks 116 }
1433    
1434     /*
1435     coverage reports
1436     */
1437    
1438     struct FileCoverageList {
1439     FileCoverage * file_coverage;
1440     struct FileCoverageList * next;
1441     };
1442    
1443     struct Coverage {
1444     JSHashTable * coverage_table;
1445     struct FileCoverageList * coverage_list;
1446     };
1447    
1448     static int compare_strings(const void * p1, const void * p2) {
1449     return strcmp(p1, p2) == 0;
1450     }
1451    
1452     Coverage * Coverage_new(void) {
1453     Coverage * result = xmalloc(sizeof(Coverage));
1454     result->coverage_table = JS_NewHashTable(1024, JS_HashString, compare_strings, NULL, NULL, NULL);
1455     if (result->coverage_table == NULL) {
1456     fatal("cannot create hash table");
1457     }
1458     result->coverage_list = NULL;
1459     return result;
1460     }
1461    
1462     void Coverage_delete(Coverage * coverage) {
1463     JS_HashTableDestroy(coverage->coverage_table);
1464     struct FileCoverageList * p = coverage->coverage_list;
1465     while (p != NULL) {
1466 siliconforks 179 free(p->file_coverage->coverage_lines);
1467     if (p->file_coverage->source_lines != NULL) {
1468     for (uint32 i = 0; i < p->file_coverage->num_source_lines; i++) {
1469     free(p->file_coverage->source_lines[i]);
1470     }
1471     free(p->file_coverage->source_lines);
1472     }
1473 siliconforks 116 free(p->file_coverage->id);
1474     free(p->file_coverage);
1475     struct FileCoverageList * q = p;
1476     p = p->next;
1477     free(q);
1478     }
1479     free(coverage);
1480     }
1481    
1482     struct EnumeratorArg {
1483     CoverageForeachFunction f;
1484     void * p;
1485     };
1486    
1487     static intN enumerator(JSHashEntry * entry, intN i, void * arg) {
1488     struct EnumeratorArg * enumerator_arg = arg;
1489     enumerator_arg->f(entry->value, i, enumerator_arg->p);
1490     return 0;
1491     }
1492    
1493     void Coverage_foreach_file(Coverage * coverage, CoverageForeachFunction f, void * p) {
1494     struct EnumeratorArg enumerator_arg;
1495     enumerator_arg.f = f;
1496     enumerator_arg.p = p;
1497     JS_HashTableEnumerateEntries(coverage->coverage_table, enumerator, &enumerator_arg);
1498     }
1499    
1500     int jscoverage_parse_json(Coverage * coverage, const uint8_t * json, size_t length) {
1501 siliconforks 336 int result = 0;
1502    
1503 siliconforks 116 jschar * base = js_InflateString(context, (char *) json, &length);
1504     if (base == NULL) {
1505     fatal("out of memory");
1506     }
1507    
1508     jschar * parenthesized_json = xnew(jschar, addst(length, 2));
1509     parenthesized_json[0] = '(';
1510     memcpy(parenthesized_json + 1, base, mulst(length, sizeof(jschar)));
1511     parenthesized_json[length + 1] = ')';
1512    
1513     JS_free(context, base);
1514    
1515 siliconforks 336 JSParseContext parse_context;
1516     if (! js_InitParseContext(context, &parse_context, NULL, NULL, parenthesized_json, length + 2, NULL, NULL, 1)) {
1517     free(parenthesized_json);
1518     return -1;
1519 siliconforks 116 }
1520 siliconforks 336 JSParseNode * root = js_ParseScript(context, global, &parse_context);
1521 siliconforks 116 free(parenthesized_json);
1522     if (root == NULL) {
1523 siliconforks 336 result = -1;
1524     goto done;
1525 siliconforks 116 }
1526    
1527     /* root node must be TOK_LC */
1528     if (root->pn_type != TOK_LC) {
1529 siliconforks 336 result = -1;
1530     goto done;
1531 siliconforks 116 }
1532     JSParseNode * semi = root->pn_u.list.head;
1533    
1534     /* the list must be TOK_SEMI and it must contain only one element */
1535     if (semi->pn_type != TOK_SEMI || semi->pn_next != NULL) {
1536 siliconforks 336 result = -1;
1537     goto done;
1538 siliconforks 116 }
1539     JSParseNode * parenthesized = semi->pn_kid;
1540    
1541     /* this must be a parenthesized expression */
1542     if (parenthesized->pn_type != TOK_RP) {
1543 siliconforks 336 result = -1;
1544     goto done;
1545 siliconforks 116 }
1546     JSParseNode * object = parenthesized->pn_kid;
1547    
1548     /* this must be an object literal */
1549     if (object->pn_type != TOK_RC) {
1550 siliconforks 336 result = -1;
1551     goto done;
1552 siliconforks 116 }
1553    
1554     for (JSParseNode * p = object->pn_head; p != NULL; p = p->pn_next) {
1555     /* every element of this list must be TOK_COLON */
1556     if (p->pn_type != TOK_COLON) {
1557 siliconforks 336 result = -1;
1558     goto done;
1559 siliconforks 116 }
1560    
1561     /* the key must be a string representing the file */
1562     JSParseNode * key = p->pn_left;
1563     if (key->pn_type != TOK_STRING || ! ATOM_IS_STRING(key->pn_atom)) {
1564 siliconforks 336 result = -1;
1565     goto done;
1566 siliconforks 116 }
1567     char * id_bytes = JS_GetStringBytes(ATOM_TO_STRING(key->pn_atom));
1568    
1569     /* the value must be an object literal OR an array */
1570     JSParseNode * value = p->pn_right;
1571     if (! (value->pn_type == TOK_RC || value->pn_type == TOK_RB)) {
1572 siliconforks 336 result = -1;
1573     goto done;
1574 siliconforks 116 }
1575    
1576     JSParseNode * array = NULL;
1577     JSParseNode * source = NULL;
1578     if (value->pn_type == TOK_RB) {
1579     /* an array */
1580     array = value;
1581     }
1582     else if (value->pn_type == TOK_RC) {
1583     /* an object literal */
1584     if (value->pn_count != 2) {
1585 siliconforks 336 result = -1;
1586     goto done;
1587 siliconforks 116 }
1588     for (JSParseNode * element = value->pn_head; element != NULL; element = element->pn_next) {
1589     if (element->pn_type != TOK_COLON) {
1590 siliconforks 336 result = -1;
1591     goto done;
1592 siliconforks 116 }
1593     JSParseNode * left = element->pn_left;
1594     if (left->pn_type != TOK_STRING || ! ATOM_IS_STRING(left->pn_atom)) {
1595 siliconforks 336 result = -1;
1596     goto done;
1597 siliconforks 116 }
1598     const char * s = JS_GetStringBytes(ATOM_TO_STRING(left->pn_atom));
1599     if (strcmp(s, "coverage") == 0) {
1600     array = element->pn_right;
1601     if (array->pn_type != TOK_RB) {
1602 siliconforks 336 result = -1;
1603     goto done;
1604 siliconforks 116 }
1605     }
1606     else if (strcmp(s, "source") == 0) {
1607     source = element->pn_right;
1608 siliconforks 179 if (source->pn_type != TOK_RB) {
1609 siliconforks 336 result = -1;
1610     goto done;
1611 siliconforks 116 }
1612     }
1613     else {
1614 siliconforks 336 result = -1;
1615     goto done;
1616 siliconforks 116 }
1617     }
1618     }
1619     else {
1620 siliconforks 336 result = -1;
1621     goto done;
1622 siliconforks 116 }
1623    
1624     if (array == NULL) {
1625 siliconforks 336 result = -1;
1626     goto done;
1627 siliconforks 116 }
1628    
1629     /* look up the file in the coverage table */
1630     FileCoverage * file_coverage = JS_HashTableLookup(coverage->coverage_table, id_bytes);
1631     if (file_coverage == NULL) {
1632     /* not there: create a new one */
1633     char * id = xstrdup(id_bytes);
1634     file_coverage = xmalloc(sizeof(FileCoverage));
1635     file_coverage->id = id;
1636 siliconforks 179 file_coverage->num_coverage_lines = array->pn_count;
1637     file_coverage->coverage_lines = xnew(int, array->pn_count);
1638 siliconforks 245 file_coverage->source_lines = NULL;
1639 siliconforks 116
1640     /* set coverage for all lines */
1641     uint32 i = 0;
1642     for (JSParseNode * element = array->pn_head; element != NULL; element = element->pn_next, i++) {
1643     if (element->pn_type == TOK_NUMBER) {
1644 siliconforks 179 file_coverage->coverage_lines[i] = (int) element->pn_dval;
1645 siliconforks 116 }
1646     else if (element->pn_type == TOK_PRIMARY && element->pn_op == JSOP_NULL) {
1647 siliconforks 179 file_coverage->coverage_lines[i] = -1;
1648 siliconforks 116 }
1649     else {
1650 siliconforks 336 result = -1;
1651     goto done;
1652 siliconforks 116 }
1653     }
1654     assert(i == array->pn_count);
1655    
1656     /* add to the hash table */
1657     JS_HashTableAdd(coverage->coverage_table, id, file_coverage);
1658     struct FileCoverageList * coverage_list = xmalloc(sizeof(struct FileCoverageList));
1659     coverage_list->file_coverage = file_coverage;
1660     coverage_list->next = coverage->coverage_list;
1661     coverage->coverage_list = coverage_list;
1662     }
1663     else {
1664     /* sanity check */
1665     assert(strcmp(file_coverage->id, id_bytes) == 0);
1666 siliconforks 179 if (file_coverage->num_coverage_lines != array->pn_count) {
1667 siliconforks 336 result = -2;
1668     goto done;
1669 siliconforks 116 }
1670    
1671     /* merge the coverage */
1672     uint32 i = 0;
1673     for (JSParseNode * element = array->pn_head; element != NULL; element = element->pn_next, i++) {
1674     if (element->pn_type == TOK_NUMBER) {
1675 siliconforks 179 if (file_coverage->coverage_lines[i] == -1) {
1676 siliconforks 336 result = -2;
1677     goto done;
1678 siliconforks 116 }
1679 siliconforks 179 file_coverage->coverage_lines[i] += (int) element->pn_dval;
1680 siliconforks 116 }
1681     else if (element->pn_type == TOK_PRIMARY && element->pn_op == JSOP_NULL) {
1682 siliconforks 179 if (file_coverage->coverage_lines[i] != -1) {
1683 siliconforks 336 result = -2;
1684     goto done;
1685 siliconforks 116 }
1686     }
1687     else {
1688 siliconforks 336 result = -1;
1689     goto done;
1690 siliconforks 116 }
1691     }
1692     assert(i == array->pn_count);
1693 siliconforks 245 }
1694 siliconforks 116
1695 siliconforks 245 /* if this JSON file has source, use it */
1696     if (file_coverage->source_lines == NULL && source != NULL) {
1697     file_coverage->num_source_lines = source->pn_count;
1698     file_coverage->source_lines = xnew(char *, source->pn_count);
1699     uint32 i = 0;
1700     for (JSParseNode * element = source->pn_head; element != NULL; element = element->pn_next, i++) {
1701     if (element->pn_type != TOK_STRING) {
1702 siliconforks 336 result = -1;
1703     goto done;
1704 siliconforks 179 }
1705 siliconforks 245 file_coverage->source_lines[i] = xstrdup(JS_GetStringBytes(ATOM_TO_STRING(element->pn_atom)));
1706 siliconforks 116 }
1707 siliconforks 245 assert(i == source->pn_count);
1708 siliconforks 116 }
1709     }
1710    
1711 siliconforks 336 done:
1712     js_FinishParseContext(context, &parse_context);
1713     return result;
1714 siliconforks 116 }

  ViewVC Help
Powered by ViewVC 1.1.24