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

Annotation of /trunk/instrument-js.c

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC 1.1.24