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

Annotation of /trunk/instrument-js.c

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC 1.1.24