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

Annotation of /trunk/instrument-js.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 179 - (hide annotations)
Sun Sep 21 18:35:21 2008 UTC (11 years, 1 month ago) by siliconforks
Original Path: trunk/instrument-js.c
File MIME type: text/plain
File size: 40629 byte(s)
Do source code highlighting during instrumentation.

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 179 void jscoverage_instrument_js(const char * id, const uint16_t * characters, size_t num_characters, Stream * output) {
889 siliconforks 2 file_id = id;
890    
891     /* scan the javascript */
892 siliconforks 179 JSTokenStream * token_stream = js_NewTokenStream(context, characters, num_characters, NULL, 1, NULL);
893 siliconforks 2 if (token_stream == NULL) {
894     fatal("cannot create token stream from file: %s", file_id);
895     }
896    
897     /* parse the javascript */
898     JSParseNode * node = js_ParseTokenStream(context, global, token_stream);
899     if (node == NULL) {
900     fatal("parse error in file: %s", file_id);
901     }
902     int num_lines = node->pn_pos.end.lineno;
903     lines = xmalloc(num_lines);
904     for (int i = 0; i < num_lines; i++) {
905     lines[i] = 0;
906     }
907    
908     /*
909 siliconforks 92 An instrumented JavaScript file has 3 sections:
910     1. initialization
911     2. instrumented source code
912 siliconforks 116 3. original source code
913 siliconforks 2 */
914    
915 siliconforks 92 Stream * instrumented = Stream_new(0);
916     instrument_statement(node, instrumented, 0);
917 siliconforks 2
918     /* write line number info to the output */
919 siliconforks 92 Stream_write_string(output, "/* automatically generated by JSCoverage - do not edit */\n");
920     Stream_write_string(output, "if (! top._$jscoverage) {\n top._$jscoverage = {};\n}\n");
921     Stream_write_string(output, "var _$jscoverage = top._$jscoverage;\n");
922     Stream_printf(output, "if (! _$jscoverage['%s']) {\n", file_id);
923     Stream_printf(output, " _$jscoverage['%s'] = [];\n", file_id);
924 siliconforks 2 for (int i = 0; i < num_lines; i++) {
925     if (lines[i]) {
926 siliconforks 92 Stream_printf(output, " _$jscoverage['%s'][%d] = 0;\n", file_id, i + 1);
927 siliconforks 2 }
928     }
929 siliconforks 92 Stream_write_string(output, "}\n");
930 siliconforks 90 free(lines);
931 siliconforks 2 lines = NULL;
932    
933 siliconforks 159 /* copy the instrumented source code to the output */
934     Stream_write(output, instrumented->data, instrumented->length);
935    
936 siliconforks 157 /* conditionals */
937     bool has_conditionals = false;
938     size_t line_number = 0;
939     size_t i = 0;
940 siliconforks 176 while (i < num_characters) {
941 siliconforks 157 line_number++;
942     size_t line_start = i;
943 siliconforks 179 /* FIXME */
944     while (i < num_characters && characters[i] != '\r' && characters[i] != '\n') {
945 siliconforks 157 i++;
946     }
947     size_t line_end = i;
948 siliconforks 176 if (i < num_characters) {
949 siliconforks 179 if (characters[i] == '\r') {
950 siliconforks 157 line_end = i;
951     i++;
952 siliconforks 179 if (i < num_characters && characters[i] == '\n') {
953 siliconforks 157 i++;
954     }
955     }
956 siliconforks 179 else if (characters[i] == '\n') {
957 siliconforks 157 line_end = i;
958     i++;
959     }
960     else {
961     abort();
962     }
963     }
964 siliconforks 179 char * line = js_DeflateString(context, characters + line_start, line_end - line_start);
965 siliconforks 157 if (str_starts_with(line, "//#JSCOVERAGE_IF")) {
966     if (! has_conditionals) {
967     has_conditionals = true;
968     Stream_printf(output, "_$jscoverage['%s'].conditionals = [];\n", file_id);
969     }
970 siliconforks 159 Stream_printf(output, "if (!%s) {\n", line + 16);
971     Stream_printf(output, " _$jscoverage['%s'].conditionals[%d] = ", file_id, line_number);
972 siliconforks 157 }
973     else if (str_starts_with(line, "//#JSCOVERAGE_ENDIF")) {
974 siliconforks 159 Stream_printf(output, "%d;\n", line_number);
975     Stream_printf(output, "}\n");
976 siliconforks 157 }
977     JS_free(context, line);
978     }
979    
980 siliconforks 95 /* copy the original source to the output */
981 siliconforks 179 Stream_printf(output, "_$jscoverage['%s'].source = ", file_id);
982     jscoverage_write_source(id, characters, num_characters, output);
983     Stream_printf(output, ";\n");
984 siliconforks 95
985 siliconforks 179 Stream_delete(instrumented);
986    
987     file_id = NULL;
988     }
989    
990     void jscoverage_write_source(const char * id, const jschar * characters, size_t num_characters, Stream * output) {
991     Stream_write_string(output, "[");
992     if (jscoverage_highlight) {
993     Stream * highlighted_stream = Stream_new(num_characters);
994     jscoverage_highlight_js(context, id, characters, num_characters, highlighted_stream);
995     size_t i = 0;
996     while (i < highlighted_stream->length) {
997     if (i > 0) {
998     Stream_write_char(output, ',');
999     }
1000    
1001     Stream_write_char(output, '"');
1002     bool done = false;
1003     while (! done) {
1004     char c = highlighted_stream->data[i];
1005     switch (c) {
1006     case 0x8:
1007     /* backspace */
1008     Stream_write_string(output, "\\b");
1009     break;
1010     case 0x9:
1011     /* horizontal tab */
1012     Stream_write_string(output, "\\t");
1013     break;
1014     case 0xa:
1015     /* line feed (new line) */
1016     done = true;
1017     break;
1018     case 0xb:
1019     /* vertical tab */
1020     Stream_write_string(output, "\\v");
1021     break;
1022     case 0xc:
1023     /* form feed */
1024     Stream_write_string(output, "\\f");
1025     break;
1026     case 0xd:
1027     /* carriage return */
1028     done = true;
1029     if (i + 1 < highlighted_stream->length && highlighted_stream->data[i + 1] == '\n') {
1030     i++;
1031     }
1032     break;
1033     case '"':
1034     Stream_write_string(output, "\\\"");
1035     break;
1036     case '\\':
1037     Stream_write_string(output, "\\\\");
1038     break;
1039     default:
1040     Stream_write_char(output, c);
1041     break;
1042     }
1043 siliconforks 95 i++;
1044 siliconforks 179 if (i >= highlighted_stream->length) {
1045     done = true;
1046 siliconforks 95 }
1047     }
1048 siliconforks 179 Stream_write_char(output, '"');
1049     }
1050     Stream_delete(highlighted_stream);
1051     }
1052     else {
1053     size_t i = 0;
1054     while (i < num_characters) {
1055     if (i > 0) {
1056     Stream_write_char(output, ',');
1057     }
1058    
1059     Stream_write_char(output, '"');
1060     bool done = false;
1061     while (! done) {
1062     jschar c = characters[i];
1063     switch (c) {
1064     case 0x8:
1065     /* backspace */
1066     Stream_write_string(output, "\\b");
1067     break;
1068     case 0x9:
1069     /* horizontal tab */
1070     Stream_write_string(output, "\\t");
1071     break;
1072     case 0xa:
1073     /* line feed (new line) */
1074     done = true;
1075     break;
1076     case 0xb:
1077     /* vertical tab */
1078     Stream_write_string(output, "\\v");
1079     break;
1080     case 0xc:
1081     /* form feed */
1082     Stream_write_string(output, "\\f");
1083     break;
1084     case 0xd:
1085     /* carriage return */
1086     done = true;
1087     if (i + 1 < num_characters && characters[i + 1] == '\n') {
1088     i++;
1089     }
1090     break;
1091     case '"':
1092     Stream_write_string(output, "\\\"");
1093     break;
1094     case '\\':
1095     Stream_write_string(output, "\\\\");
1096     break;
1097     case '&':
1098     Stream_write_string(output, "&amp;");
1099     break;
1100     case '<':
1101     Stream_write_string(output, "&lt;");
1102     break;
1103     case '>':
1104     Stream_write_string(output, "&gt;");
1105     break;
1106     case 0x2028:
1107     case 0x2029:
1108     done = true;
1109     break;
1110     default:
1111     if (32 <= c && c <= 126) {
1112     Stream_write_char(output, c);
1113     }
1114     else {
1115     Stream_printf(output, "&#%d;", c);
1116     }
1117     break;
1118     }
1119 siliconforks 95 i++;
1120 siliconforks 179 if (i >= num_characters) {
1121     done = true;
1122     }
1123 siliconforks 95 }
1124 siliconforks 179 Stream_write_char(output, '"');
1125 siliconforks 95 }
1126     }
1127 siliconforks 179 Stream_write_string(output, "]");
1128 siliconforks 2 }
1129 siliconforks 116
1130     void jscoverage_copy_resources(const char * destination_directory) {
1131     copy_resource("jscoverage.html", destination_directory);
1132     copy_resource("jscoverage.css", destination_directory);
1133     copy_resource("jscoverage.js", destination_directory);
1134 siliconforks 169 copy_resource("jscoverage-ie.css", destination_directory);
1135 siliconforks 116 copy_resource("jscoverage-throbber.gif", destination_directory);
1136 siliconforks 179 copy_resource("jscoverage-highlight.css", destination_directory);
1137 siliconforks 116 }
1138    
1139     /*
1140     coverage reports
1141     */
1142    
1143     struct FileCoverageList {
1144     FileCoverage * file_coverage;
1145     struct FileCoverageList * next;
1146     };
1147    
1148     struct Coverage {
1149     JSHashTable * coverage_table;
1150     struct FileCoverageList * coverage_list;
1151     };
1152    
1153     static int compare_strings(const void * p1, const void * p2) {
1154     return strcmp(p1, p2) == 0;
1155     }
1156    
1157     Coverage * Coverage_new(void) {
1158     Coverage * result = xmalloc(sizeof(Coverage));
1159     result->coverage_table = JS_NewHashTable(1024, JS_HashString, compare_strings, NULL, NULL, NULL);
1160     if (result->coverage_table == NULL) {
1161     fatal("cannot create hash table");
1162     }
1163     result->coverage_list = NULL;
1164     return result;
1165     }
1166    
1167     void Coverage_delete(Coverage * coverage) {
1168     JS_HashTableDestroy(coverage->coverage_table);
1169     struct FileCoverageList * p = coverage->coverage_list;
1170     while (p != NULL) {
1171 siliconforks 179 free(p->file_coverage->coverage_lines);
1172     if (p->file_coverage->source_lines != NULL) {
1173     for (uint32 i = 0; i < p->file_coverage->num_source_lines; i++) {
1174     free(p->file_coverage->source_lines[i]);
1175     }
1176     free(p->file_coverage->source_lines);
1177     }
1178 siliconforks 116 free(p->file_coverage->id);
1179     free(p->file_coverage);
1180     struct FileCoverageList * q = p;
1181     p = p->next;
1182     free(q);
1183     }
1184     free(coverage);
1185     }
1186    
1187     struct EnumeratorArg {
1188     CoverageForeachFunction f;
1189     void * p;
1190     };
1191    
1192     static intN enumerator(JSHashEntry * entry, intN i, void * arg) {
1193     struct EnumeratorArg * enumerator_arg = arg;
1194     enumerator_arg->f(entry->value, i, enumerator_arg->p);
1195     return 0;
1196     }
1197    
1198     void Coverage_foreach_file(Coverage * coverage, CoverageForeachFunction f, void * p) {
1199     struct EnumeratorArg enumerator_arg;
1200     enumerator_arg.f = f;
1201     enumerator_arg.p = p;
1202     JS_HashTableEnumerateEntries(coverage->coverage_table, enumerator, &enumerator_arg);
1203     }
1204    
1205     int jscoverage_parse_json(Coverage * coverage, const uint8_t * json, size_t length) {
1206     jschar * base = js_InflateString(context, (char *) json, &length);
1207     if (base == NULL) {
1208     fatal("out of memory");
1209     }
1210    
1211     jschar * parenthesized_json = xnew(jschar, addst(length, 2));
1212     parenthesized_json[0] = '(';
1213     memcpy(parenthesized_json + 1, base, mulst(length, sizeof(jschar)));
1214     parenthesized_json[length + 1] = ')';
1215    
1216     JS_free(context, base);
1217    
1218     JSTokenStream * token_stream = js_NewTokenStream(context, parenthesized_json, length + 2, NULL, 1, NULL);
1219     if (token_stream == NULL) {
1220     fatal("cannot create token stream");
1221     }
1222    
1223     JSParseNode * root = js_ParseTokenStream(context, global, token_stream);
1224     free(parenthesized_json);
1225     if (root == NULL) {
1226     return -1;
1227     }
1228    
1229     /* root node must be TOK_LC */
1230     if (root->pn_type != TOK_LC) {
1231     return -1;
1232     }
1233     JSParseNode * semi = root->pn_u.list.head;
1234    
1235     /* the list must be TOK_SEMI and it must contain only one element */
1236     if (semi->pn_type != TOK_SEMI || semi->pn_next != NULL) {
1237     return -1;
1238     }
1239     JSParseNode * parenthesized = semi->pn_kid;
1240    
1241     /* this must be a parenthesized expression */
1242     if (parenthesized->pn_type != TOK_RP) {
1243     return -1;
1244     }
1245     JSParseNode * object = parenthesized->pn_kid;
1246    
1247     /* this must be an object literal */
1248     if (object->pn_type != TOK_RC) {
1249     return -1;
1250     }
1251    
1252     for (JSParseNode * p = object->pn_head; p != NULL; p = p->pn_next) {
1253     /* every element of this list must be TOK_COLON */
1254     if (p->pn_type != TOK_COLON) {
1255     return -1;
1256     }
1257    
1258     /* the key must be a string representing the file */
1259     JSParseNode * key = p->pn_left;
1260     if (key->pn_type != TOK_STRING || ! ATOM_IS_STRING(key->pn_atom)) {
1261     return -1;
1262     }
1263     char * id_bytes = JS_GetStringBytes(ATOM_TO_STRING(key->pn_atom));
1264    
1265     /* the value must be an object literal OR an array */
1266     JSParseNode * value = p->pn_right;
1267     if (! (value->pn_type == TOK_RC || value->pn_type == TOK_RB)) {
1268     return -1;
1269     }
1270    
1271     JSParseNode * array = NULL;
1272     JSParseNode * source = NULL;
1273     if (value->pn_type == TOK_RB) {
1274     /* an array */
1275     array = value;
1276     }
1277     else if (value->pn_type == TOK_RC) {
1278     /* an object literal */
1279     if (value->pn_count != 2) {
1280     return -1;
1281     }
1282     for (JSParseNode * element = value->pn_head; element != NULL; element = element->pn_next) {
1283     if (element->pn_type != TOK_COLON) {
1284     return -1;
1285     }
1286     JSParseNode * left = element->pn_left;
1287     if (left->pn_type != TOK_STRING || ! ATOM_IS_STRING(left->pn_atom)) {
1288     return -1;
1289     }
1290     const char * s = JS_GetStringBytes(ATOM_TO_STRING(left->pn_atom));
1291     if (strcmp(s, "coverage") == 0) {
1292     array = element->pn_right;
1293     if (array->pn_type != TOK_RB) {
1294     return -1;
1295     }
1296     }
1297     else if (strcmp(s, "source") == 0) {
1298     source = element->pn_right;
1299 siliconforks 179 if (source->pn_type != TOK_RB) {
1300 siliconforks 116 return -1;
1301     }
1302     }
1303     else {
1304     return -1;
1305     }
1306     }
1307     }
1308     else {
1309     return -1;
1310     }
1311    
1312     if (array == NULL) {
1313     return -1;
1314     }
1315    
1316     /* look up the file in the coverage table */
1317     FileCoverage * file_coverage = JS_HashTableLookup(coverage->coverage_table, id_bytes);
1318     if (file_coverage == NULL) {
1319     /* not there: create a new one */
1320     char * id = xstrdup(id_bytes);
1321     file_coverage = xmalloc(sizeof(FileCoverage));
1322     file_coverage->id = id;
1323 siliconforks 179 file_coverage->num_coverage_lines = array->pn_count;
1324     file_coverage->coverage_lines = xnew(int, array->pn_count);
1325 siliconforks 116 if (source == NULL) {
1326 siliconforks 179 file_coverage->source_lines = NULL;
1327 siliconforks 116 }
1328     else {
1329 siliconforks 179 file_coverage->num_source_lines = source->pn_count;
1330     file_coverage->source_lines = xnew(char *, source->pn_count);
1331     uint32 i = 0;
1332     for (JSParseNode * element = source->pn_head; element != NULL; element = element->pn_next, i++) {
1333     if (element->pn_type != TOK_STRING) {
1334     return -1;
1335     }
1336     file_coverage->source_lines[i] = xstrdup(JS_GetStringBytes(ATOM_TO_STRING(element->pn_atom)));
1337     }
1338     assert(i == source->pn_count);
1339 siliconforks 116 }
1340    
1341     /* set coverage for all lines */
1342     uint32 i = 0;
1343     for (JSParseNode * element = array->pn_head; element != NULL; element = element->pn_next, i++) {
1344     if (element->pn_type == TOK_NUMBER) {
1345 siliconforks 179 file_coverage->coverage_lines[i] = (int) element->pn_dval;
1346 siliconforks 116 }
1347     else if (element->pn_type == TOK_PRIMARY && element->pn_op == JSOP_NULL) {
1348 siliconforks 179 file_coverage->coverage_lines[i] = -1;
1349 siliconforks 116 }
1350     else {
1351     return -1;
1352     }
1353     }
1354     assert(i == array->pn_count);
1355    
1356     /* add to the hash table */
1357     JS_HashTableAdd(coverage->coverage_table, id, file_coverage);
1358     struct FileCoverageList * coverage_list = xmalloc(sizeof(struct FileCoverageList));
1359     coverage_list->file_coverage = file_coverage;
1360     coverage_list->next = coverage->coverage_list;
1361     coverage->coverage_list = coverage_list;
1362     }
1363     else {
1364     /* sanity check */
1365     assert(strcmp(file_coverage->id, id_bytes) == 0);
1366 siliconforks 179 if (file_coverage->num_coverage_lines != array->pn_count) {
1367 siliconforks 116 return -2;
1368     }
1369    
1370     /* merge the coverage */
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 if (file_coverage->coverage_lines[i] == -1) {
1375 siliconforks 116 return -2;
1376     }
1377 siliconforks 179 file_coverage->coverage_lines[i] += (int) element->pn_dval;
1378 siliconforks 116 }
1379     else if (element->pn_type == TOK_PRIMARY && element->pn_op == JSOP_NULL) {
1380 siliconforks 179 if (file_coverage->coverage_lines[i] != -1) {
1381 siliconforks 116 return -2;
1382     }
1383     }
1384     else {
1385     return -1;
1386     }
1387     }
1388     assert(i == array->pn_count);
1389    
1390     /* if this JSON file has source, use it */
1391 siliconforks 179 if (file_coverage->source_lines == NULL && source != NULL) {
1392     file_coverage->num_source_lines = source->pn_count;
1393     file_coverage->source_lines = xnew(char *, source->pn_count);
1394     uint32 i = 0;
1395     for (JSParseNode * element = source->pn_head; element != NULL; element = element->pn_next, i++) {
1396     if (element->pn_type != TOK_STRING) {
1397     return -1;
1398     }
1399     file_coverage->source_lines[i] = xstrdup(JS_GetStringBytes(ATOM_TO_STRING(element->pn_atom)));
1400     }
1401     assert(i == source->pn_count);
1402 siliconforks 116 }
1403     }
1404     }
1405    
1406     return 0;
1407     }

  ViewVC Help
Powered by ViewVC 1.1.24