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

Annotation of /trunk/instrument-js.c

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC 1.1.24