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

Annotation of /trunk/instrument-js.c

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC 1.1.24