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

Annotation of /trunk/instrument-js.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 521 - (hide annotations)
Wed Jan 13 18:53:04 2010 UTC (9 years, 11 months ago) by siliconforks
File size: 59177 byte(s)
Parenthesize "let" and "yield" expressions.

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

Properties

Name Value
svn:mergeinfo

  ViewVC Help
Powered by ViewVC 1.1.24