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

Annotation of /trunk/instrument-js.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 504 - (hide annotations)
Mon Dec 28 04:59:47 2009 UTC (8 years, 11 months ago) by siliconforks
File size: 58204 byte(s)
Eliminate some compilation warnings.

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

Properties

Name Value
svn:mergeinfo

  ViewVC Help
Powered by ViewVC 1.1.24