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

Annotation of /trunk/instrument-js.cpp

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC 1.1.24