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

Annotation of /trunk/instrument-js.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 517 - (hide annotations)
Wed Jan 13 18:04:49 2010 UTC (8 years, 9 months ago) by siliconforks
File size: 58998 byte(s)
Fix operator precedence bug.

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     /* TOK_COMMA is a special case: a hole in the array */
821     if (p->pn_type != TOK_COMMA) {
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     Stream_write_string(f, "let(");
959     assert(node->pn_expr->pn_left->pn_type == TOK_LP);
960     assert(node->pn_expr->pn_left->pn_arity == PN_LIST);
961     instrument_declarations(node->pn_expr->pn_left, f);
962     Stream_write_string(f, ") ");
963 siliconforks 379 output_expression(node->pn_expr->pn_right, f, true);
964 siliconforks 340 break;
965 siliconforks 342 case TOK_YIELD:
966     assert(node->pn_arity == PN_UNARY);
967 siliconforks 355 Stream_write_string(f, "yield");
968     if (node->pn_kid != NULL) {
969     Stream_write_char(f, ' ');
970 siliconforks 379 output_expression(node->pn_kid, f, true);
971 siliconforks 355 }
972 siliconforks 342 break;
973 siliconforks 343 case TOK_ARRAYCOMP:
974     assert(node->pn_arity == PN_LIST);
975     {
976     JSParseNode * block_node;
977     switch (node->pn_count) {
978     case 1:
979     block_node = node->pn_head;
980     break;
981     case 2:
982     block_node = node->pn_head->pn_next;
983     break;
984     default:
985     abort();
986     break;
987     }
988     Stream_write_char(f, '[');
989 siliconforks 357 output_array_comprehension_or_generator_expression(block_node, f);
990 siliconforks 343 Stream_write_char(f, ']');
991     }
992     break;
993     case TOK_VAR:
994     assert(node->pn_arity == PN_LIST);
995 siliconforks 481 if (node->pn_op == JSOP_DEFCONST) {
996     Stream_write_string(f, "const ");
997     }
998     else {
999     Stream_write_string(f, "var ");
1000     }
1001 siliconforks 343 instrument_declarations(node, f);
1002     break;
1003     case TOK_LET:
1004     assert(node->pn_arity == PN_LIST);
1005     Stream_write_string(f, "let ");
1006     instrument_declarations(node, f);
1007     break;
1008 siliconforks 2 default:
1009 siliconforks 504 fatal_source(file_id, node->pn_pos.begin.lineno, "unsupported node type (%u)", (unsigned int) node->pn_type);
1010 siliconforks 2 }
1011     }
1012    
1013 siliconforks 240 static void output_statement(JSParseNode * node, Stream * f, int indent, bool is_jscoverage_if) {
1014 siliconforks 2 switch (node->pn_type) {
1015     case TOK_FUNCTION:
1016 siliconforks 155 instrument_function(node, f, indent, FUNCTION_NORMAL);
1017 siliconforks 380 Stream_write_char(f, '\n');
1018 siliconforks 2 break;
1019     case TOK_LC:
1020     assert(node->pn_arity == PN_LIST);
1021     /*
1022 siliconforks 92 Stream_write_string(f, "{\n");
1023 siliconforks 2 */
1024     for (struct JSParseNode * p = node->pn_u.list.head; p != NULL; p = p->pn_next) {
1025 siliconforks 240 instrument_statement(p, f, indent, false);
1026 siliconforks 2 }
1027     /*
1028 siliconforks 92 Stream_printf(f, "%*s", indent, "");
1029     Stream_write_string(f, "}\n");
1030 siliconforks 2 */
1031     break;
1032     case TOK_IF:
1033 siliconforks 240 {
1034 siliconforks 2 assert(node->pn_arity == PN_TERNARY);
1035 siliconforks 240
1036 siliconforks 473 uint32_t line = node->pn_pos.begin.lineno;
1037 siliconforks 240 if (! is_jscoverage_if) {
1038     if (line > num_lines) {
1039 siliconforks 370 fatal("file %s contains more than 65,535 lines", file_id);
1040 siliconforks 240 }
1041     if (line >= 2 && exclusive_directives[line - 2]) {
1042     is_jscoverage_if = true;
1043     }
1044     }
1045    
1046 siliconforks 92 Stream_printf(f, "%*s", indent, "");
1047     Stream_write_string(f, "if (");
1048 siliconforks 379 output_expression(node->pn_kid1, f, false);
1049 siliconforks 92 Stream_write_string(f, ") {\n");
1050 siliconforks 240 if (is_jscoverage_if && node->pn_kid3) {
1051 siliconforks 473 uint32_t else_start = node->pn_kid3->pn_pos.begin.lineno;
1052     uint32_t else_end = node->pn_kid3->pn_pos.end.lineno + 1;
1053 siliconforks 240 Stream_printf(f, "%*s", indent + 2, "");
1054     Stream_printf(f, "_$jscoverage['%s'].conditionals[%d] = %d;\n", file_id, else_start, else_end);
1055     }
1056     instrument_statement(node->pn_kid2, f, indent + 2, false);
1057 siliconforks 92 Stream_printf(f, "%*s", indent, "");
1058     Stream_write_string(f, "}\n");
1059 siliconforks 240
1060     if (node->pn_kid3 || is_jscoverage_if) {
1061 siliconforks 92 Stream_printf(f, "%*s", indent, "");
1062     Stream_write_string(f, "else {\n");
1063 siliconforks 240
1064     if (is_jscoverage_if) {
1065 siliconforks 473 uint32_t if_start = node->pn_kid2->pn_pos.begin.lineno + 1;
1066     uint32_t if_end = node->pn_kid2->pn_pos.end.lineno + 1;
1067 siliconforks 240 Stream_printf(f, "%*s", indent + 2, "");
1068     Stream_printf(f, "_$jscoverage['%s'].conditionals[%d] = %d;\n", file_id, if_start, if_end);
1069     }
1070    
1071     if (node->pn_kid3) {
1072     instrument_statement(node->pn_kid3, f, indent + 2, is_jscoverage_if);
1073     }
1074    
1075 siliconforks 92 Stream_printf(f, "%*s", indent, "");
1076     Stream_write_string(f, "}\n");
1077 siliconforks 2 }
1078 siliconforks 240
1079 siliconforks 2 break;
1080 siliconforks 240 }
1081 siliconforks 2 case TOK_SWITCH:
1082     assert(node->pn_arity == PN_BINARY);
1083 siliconforks 92 Stream_printf(f, "%*s", indent, "");
1084     Stream_write_string(f, "switch (");
1085 siliconforks 379 output_expression(node->pn_left, f, false);
1086 siliconforks 92 Stream_write_string(f, ") {\n");
1087 siliconforks 350 {
1088     JSParseNode * list = node->pn_right;
1089     if (list->pn_type == TOK_LEXICALSCOPE) {
1090     list = list->pn_expr;
1091 siliconforks 2 }
1092 siliconforks 350 for (struct JSParseNode * p = list->pn_head; p != NULL; p = p->pn_next) {
1093     Stream_printf(f, "%*s", indent, "");
1094     switch (p->pn_type) {
1095     case TOK_CASE:
1096     Stream_write_string(f, "case ");
1097 siliconforks 379 output_expression(p->pn_left, f, false);
1098 siliconforks 350 Stream_write_string(f, ":\n");
1099     break;
1100     case TOK_DEFAULT:
1101     Stream_write_string(f, "default:\n");
1102     break;
1103     default:
1104     abort();
1105     break;
1106     }
1107     instrument_statement(p->pn_right, f, indent + 2, false);
1108     }
1109 siliconforks 2 }
1110 siliconforks 92 Stream_printf(f, "%*s", indent, "");
1111     Stream_write_string(f, "}\n");
1112 siliconforks 2 break;
1113     case TOK_CASE:
1114     case TOK_DEFAULT:
1115     abort();
1116     break;
1117     case TOK_WHILE:
1118     assert(node->pn_arity == PN_BINARY);
1119 siliconforks 92 Stream_printf(f, "%*s", indent, "");
1120     Stream_write_string(f, "while (");
1121 siliconforks 379 output_expression(node->pn_left, f, false);
1122 siliconforks 92 Stream_write_string(f, ") {\n");
1123 siliconforks 240 instrument_statement(node->pn_right, f, indent + 2, false);
1124 siliconforks 92 Stream_write_string(f, "}\n");
1125 siliconforks 2 break;
1126     case TOK_DO:
1127     assert(node->pn_arity == PN_BINARY);
1128 siliconforks 92 Stream_printf(f, "%*s", indent, "");
1129     Stream_write_string(f, "do {\n");
1130 siliconforks 240 instrument_statement(node->pn_left, f, indent + 2, false);
1131 siliconforks 92 Stream_write_string(f, "}\n");
1132     Stream_printf(f, "%*s", indent, "");
1133     Stream_write_string(f, "while (");
1134 siliconforks 379 output_expression(node->pn_right, f, false);
1135 siliconforks 92 Stream_write_string(f, ");\n");
1136 siliconforks 2 break;
1137     case TOK_FOR:
1138     assert(node->pn_arity == PN_BINARY);
1139 siliconforks 92 Stream_printf(f, "%*s", indent, "");
1140 siliconforks 2 switch (node->pn_left->pn_type) {
1141     case TOK_IN:
1142     /* for/in */
1143     assert(node->pn_left->pn_arity == PN_BINARY);
1144 siliconforks 343 output_for_in(node, f);
1145 siliconforks 2 break;
1146 siliconforks 399 case TOK_FORHEAD:
1147 siliconforks 2 /* for (;;) */
1148     assert(node->pn_left->pn_arity == PN_TERNARY);
1149 siliconforks 343 Stream_write_string(f, "for (");
1150 siliconforks 2 if (node->pn_left->pn_kid1) {
1151 siliconforks 517 output_expression(node->pn_left->pn_kid1, f, false, false);
1152 siliconforks 2 }
1153 siliconforks 92 Stream_write_string(f, ";");
1154 siliconforks 2 if (node->pn_left->pn_kid2) {
1155 siliconforks 92 Stream_write_char(f, ' ');
1156 siliconforks 379 output_expression(node->pn_left->pn_kid2, f, false);
1157 siliconforks 2 }
1158 siliconforks 92 Stream_write_string(f, ";");
1159 siliconforks 2 if (node->pn_left->pn_kid3) {
1160 siliconforks 92 Stream_write_char(f, ' ');
1161 siliconforks 379 output_expression(node->pn_left->pn_kid3, f, false);
1162 siliconforks 2 }
1163 siliconforks 343 Stream_write_char(f, ')');
1164 siliconforks 2 break;
1165     default:
1166     abort();
1167     break;
1168     }
1169 siliconforks 343 Stream_write_string(f, " {\n");
1170 siliconforks 240 instrument_statement(node->pn_right, f, indent + 2, false);
1171 siliconforks 92 Stream_write_string(f, "}\n");
1172 siliconforks 2 break;
1173     case TOK_THROW:
1174     assert(node->pn_arity == PN_UNARY);
1175 siliconforks 92 Stream_printf(f, "%*s", indent, "");
1176     Stream_write_string(f, "throw ");
1177 siliconforks 379 output_expression(node->pn_u.unary.kid, f, false);
1178 siliconforks 92 Stream_write_string(f, ";\n");
1179 siliconforks 2 break;
1180     case TOK_TRY:
1181 siliconforks 92 Stream_printf(f, "%*s", indent, "");
1182     Stream_write_string(f, "try {\n");
1183 siliconforks 240 instrument_statement(node->pn_kid1, f, indent + 2, false);
1184 siliconforks 92 Stream_printf(f, "%*s", indent, "");
1185     Stream_write_string(f, "}\n");
1186 siliconforks 336 if (node->pn_kid2) {
1187     assert(node->pn_kid2->pn_type == TOK_RESERVED);
1188     for (JSParseNode * scope = node->pn_kid2->pn_head; scope != NULL; scope = scope->pn_next) {
1189     assert(scope->pn_type == TOK_LEXICALSCOPE);
1190 siliconforks 399 JSParseNode * catch_node = scope->pn_expr;
1191     assert(catch_node->pn_type == TOK_CATCH);
1192 siliconforks 92 Stream_printf(f, "%*s", indent, "");
1193     Stream_write_string(f, "catch (");
1194 siliconforks 399 output_expression(catch_node->pn_kid1, f, false);
1195     if (catch_node->pn_kid2) {
1196 siliconforks 92 Stream_write_string(f, " if ");
1197 siliconforks 399 output_expression(catch_node->pn_kid2, f, false);
1198 siliconforks 2 }
1199 siliconforks 92 Stream_write_string(f, ") {\n");
1200 siliconforks 399 instrument_statement(catch_node->pn_kid3, f, indent + 2, false);
1201 siliconforks 92 Stream_printf(f, "%*s", indent, "");
1202     Stream_write_string(f, "}\n");
1203 siliconforks 2 }
1204     }
1205     if (node->pn_kid3) {
1206 siliconforks 92 Stream_printf(f, "%*s", indent, "");
1207     Stream_write_string(f, "finally {\n");
1208 siliconforks 240 instrument_statement(node->pn_kid3, f, indent + 2, false);
1209 siliconforks 92 Stream_printf(f, "%*s", indent, "");
1210     Stream_write_string(f, "}\n");
1211 siliconforks 2 }
1212     break;
1213     case TOK_CATCH:
1214     abort();
1215     break;
1216     case TOK_BREAK:
1217     case TOK_CONTINUE:
1218     assert(node->pn_arity == PN_NAME || node->pn_arity == PN_NULLARY);
1219 siliconforks 92 Stream_printf(f, "%*s", indent, "");
1220     Stream_write_string(f, node->pn_type == TOK_BREAK? "break": "continue");
1221 siliconforks 399 if (node->pn_atom != NULL) {
1222 siliconforks 92 Stream_write_char(f, ' ');
1223 siliconforks 2 print_string_atom(node->pn_atom, f);
1224     }
1225 siliconforks 92 Stream_write_string(f, ";\n");
1226 siliconforks 2 break;
1227     case TOK_WITH:
1228     assert(node->pn_arity == PN_BINARY);
1229 siliconforks 92 Stream_printf(f, "%*s", indent, "");
1230     Stream_write_string(f, "with (");
1231 siliconforks 379 output_expression(node->pn_left, f, false);
1232 siliconforks 92 Stream_write_string(f, ") {\n");
1233 siliconforks 240 instrument_statement(node->pn_right, f, indent + 2, false);
1234 siliconforks 92 Stream_printf(f, "%*s", indent, "");
1235     Stream_write_string(f, "}\n");
1236 siliconforks 2 break;
1237     case TOK_VAR:
1238 siliconforks 343 Stream_printf(f, "%*s", indent, "");
1239 siliconforks 379 output_expression(node, f, false);
1240 siliconforks 92 Stream_write_string(f, ";\n");
1241 siliconforks 2 break;
1242     case TOK_RETURN:
1243     assert(node->pn_arity == PN_UNARY);
1244 siliconforks 92 Stream_printf(f, "%*s", indent, "");
1245     Stream_write_string(f, "return");
1246 siliconforks 2 if (node->pn_kid != NULL) {
1247 siliconforks 92 Stream_write_char(f, ' ');
1248 siliconforks 379 output_expression(node->pn_kid, f, true);
1249 siliconforks 2 }
1250 siliconforks 92 Stream_write_string(f, ";\n");
1251 siliconforks 2 break;
1252     case TOK_SEMI:
1253     assert(node->pn_arity == PN_UNARY);
1254 siliconforks 92 Stream_printf(f, "%*s", indent, "");
1255 siliconforks 2 if (node->pn_kid != NULL) {
1256 siliconforks 517 output_expression(node->pn_kid, f, true, false);
1257 siliconforks 2 }
1258 siliconforks 92 Stream_write_string(f, ";\n");
1259 siliconforks 2 break;
1260     case TOK_COLON:
1261 siliconforks 376 {
1262 siliconforks 2 assert(node->pn_arity == PN_NAME);
1263 siliconforks 92 Stream_printf(f, "%*s", indent < 2? 0: indent - 2, "");
1264 siliconforks 2 print_string_atom(node->pn_atom, f);
1265 siliconforks 92 Stream_write_string(f, ":\n");
1266 siliconforks 376 JSParseNode * labelled = node->pn_expr;
1267     if (labelled->pn_type == TOK_LEXICALSCOPE) {
1268     labelled = labelled->pn_expr;
1269     }
1270     if (labelled->pn_type == TOK_LC) {
1271     /* labelled block */
1272     Stream_printf(f, "%*s", indent, "");
1273     Stream_write_string(f, "{\n");
1274     instrument_statement(labelled, f, indent + 2, false);
1275     Stream_printf(f, "%*s", indent, "");
1276     Stream_write_string(f, "}\n");
1277     }
1278     else {
1279     /*
1280     This one is tricky: can't output instrumentation between the label and the
1281     statement it's supposed to label, so use output_statement instead of
1282     instrument_statement.
1283     */
1284     output_statement(labelled, f, indent, false);
1285     }
1286 siliconforks 2 break;
1287 siliconforks 376 }
1288 siliconforks 340 case TOK_LEXICALSCOPE:
1289     /* let statement */
1290     assert(node->pn_arity == PN_NAME);
1291     switch (node->pn_expr->pn_type) {
1292     case TOK_LET:
1293     /* let statement */
1294     assert(node->pn_expr->pn_arity == PN_BINARY);
1295     instrument_statement(node->pn_expr, f, indent, false);
1296     break;
1297     case TOK_LC:
1298     /* block */
1299     Stream_printf(f, "%*s", indent, "");
1300     Stream_write_string(f, "{\n");
1301     instrument_statement(node->pn_expr, f, indent + 2, false);
1302     Stream_printf(f, "%*s", indent, "");
1303     Stream_write_string(f, "}\n");
1304     break;
1305     case TOK_FOR:
1306     instrument_statement(node->pn_expr, f, indent, false);
1307     break;
1308     default:
1309     abort();
1310     break;
1311     }
1312     break;
1313     case TOK_LET:
1314     switch (node->pn_arity) {
1315     case PN_BINARY:
1316     /* let statement */
1317     Stream_printf(f, "%*s", indent, "");
1318     Stream_write_string(f, "let (");
1319     assert(node->pn_left->pn_type == TOK_LP);
1320     assert(node->pn_left->pn_arity == PN_LIST);
1321     instrument_declarations(node->pn_left, f);
1322     Stream_write_string(f, ") {\n");
1323     instrument_statement(node->pn_right, f, indent + 2, false);
1324     Stream_printf(f, "%*s", indent, "");
1325     Stream_write_string(f, "}\n");
1326     break;
1327     case PN_LIST:
1328     /* let definition */
1329 siliconforks 343 Stream_printf(f, "%*s", indent, "");
1330 siliconforks 379 output_expression(node, f, false);
1331 siliconforks 340 Stream_write_string(f, ";\n");
1332     break;
1333     default:
1334     abort();
1335     break;
1336     }
1337     break;
1338 siliconforks 348 case TOK_DEBUGGER:
1339     Stream_printf(f, "%*s", indent, "");
1340     Stream_write_string(f, "debugger;\n");
1341     break;
1342 siliconforks 401 case TOK_SEQ:
1343     /*
1344     This occurs with the statement:
1345     for (var a = b in c) {}
1346     */
1347     assert(node->pn_arity == PN_LIST);
1348     for (JSParseNode * p = node->pn_head; p != NULL; p = p->pn_next) {
1349     instrument_statement(p, f, indent, false);
1350     }
1351     break;
1352 siliconforks 460 case TOK_NAME:
1353     // this is a duplicate function
1354 siliconforks 463 // FIXME
1355 siliconforks 460 break;
1356 siliconforks 2 default:
1357 siliconforks 504 fatal_source(file_id, node->pn_pos.begin.lineno, "unsupported node type (%u)", (unsigned int) node->pn_type);
1358 siliconforks 2 }
1359     }
1360    
1361     /*
1362     See <Statements> in jsparse.h.
1363     TOK_FUNCTION is handled as a statement and as an expression.
1364     TOK_EXPORT, TOK_IMPORT are not handled.
1365     */
1366 siliconforks 240 static void instrument_statement(JSParseNode * node, Stream * f, int indent, bool is_jscoverage_if) {
1367 siliconforks 340 if (node->pn_type != TOK_LC && node->pn_type != TOK_LEXICALSCOPE) {
1368 siliconforks 473 uint32_t line = node->pn_pos.begin.lineno;
1369 siliconforks 214 if (line > num_lines) {
1370 siliconforks 370 fatal("file %s contains more than 65,535 lines", file_id);
1371 siliconforks 214 }
1372    
1373 siliconforks 2 /* the root node has line number 0 */
1374     if (line != 0) {
1375 siliconforks 92 Stream_printf(f, "%*s", indent, "");
1376     Stream_printf(f, "_$jscoverage['%s'][%d]++;\n", file_id, line);
1377 siliconforks 2 lines[line - 1] = 1;
1378     }
1379     }
1380 siliconforks 240 output_statement(node, f, indent, is_jscoverage_if);
1381 siliconforks 2 }
1382    
1383 siliconforks 190 static bool characters_start_with(const jschar * characters, size_t line_start, size_t line_end, const char * prefix) {
1384     const jschar * characters_end = characters + line_end;
1385     const jschar * cp = characters + line_start;
1386     const char * bp = prefix;
1387     for (;;) {
1388     if (*bp == '\0') {
1389     return true;
1390     }
1391     else if (cp == characters_end) {
1392     return false;
1393     }
1394     else if (*cp != *bp) {
1395     return false;
1396     }
1397     bp++;
1398     cp++;
1399     }
1400     }
1401    
1402 siliconforks 240 static bool characters_are_white_space(const jschar * characters, size_t line_start, size_t line_end) {
1403     /* XXX - other Unicode space */
1404     const jschar * end = characters + line_end;
1405     for (const jschar * p = characters + line_start; p < end; p++) {
1406     jschar c = *p;
1407     if (c == 0x9 || c == 0xB || c == 0xC || c == 0x20 || c == 0xA0) {
1408     continue;
1409     }
1410     else {
1411     return false;
1412     }
1413     }
1414     return true;
1415     }
1416    
1417 siliconforks 284 static void error_reporter(JSContext * context, const char * message, JSErrorReport * report) {
1418 siliconforks 372 warn_source(file_id, report->lineno, "%s", message);
1419 siliconforks 284 }
1420    
1421 siliconforks 179 void jscoverage_instrument_js(const char * id, const uint16_t * characters, size_t num_characters, Stream * output) {
1422 siliconforks 2 file_id = id;
1423    
1424 siliconforks 336 /* parse the javascript */
1425 siliconforks 460 JSCompiler compiler(context);
1426     if (! compiler.init(characters, num_characters, NULL, id, 1)) {
1427 siliconforks 370 fatal("cannot create token stream from file %s", file_id);
1428 siliconforks 2 }
1429 siliconforks 284 JSErrorReporter old_error_reporter = JS_SetErrorReporter(context, error_reporter);
1430 siliconforks 460 JSParseNode * node = compiler.parse(global);
1431 siliconforks 2 if (node == NULL) {
1432 siliconforks 284 js_ReportUncaughtException(context);
1433 siliconforks 370 fatal("parse error in file %s", file_id);
1434 siliconforks 2 }
1435 siliconforks 284 JS_SetErrorReporter(context, old_error_reporter);
1436 siliconforks 214 num_lines = node->pn_pos.end.lineno;
1437 siliconforks 399 lines = (char *) xmalloc(num_lines);
1438 siliconforks 240 for (unsigned int i = 0; i < num_lines; i++) {
1439 siliconforks 2 lines[i] = 0;
1440     }
1441    
1442 siliconforks 240 /* search code for conditionals */
1443     exclusive_directives = xnew(bool, num_lines);
1444     for (unsigned int i = 0; i < num_lines; i++) {
1445     exclusive_directives[i] = false;
1446 siliconforks 2 }
1447    
1448 siliconforks 157 bool has_conditionals = false;
1449 siliconforks 240 struct IfDirective * if_directives = NULL;
1450 siliconforks 157 size_t line_number = 0;
1451     size_t i = 0;
1452 siliconforks 176 while (i < num_characters) {
1453 siliconforks 473 if (line_number == UINT32_MAX) {
1454 siliconforks 370 fatal("file %s contains more than 65,535 lines", file_id);
1455 siliconforks 240 }
1456 siliconforks 157 line_number++;
1457     size_t line_start = i;
1458 siliconforks 190 jschar c;
1459     bool done = false;
1460     while (! done && i < num_characters) {
1461     c = characters[i];
1462     switch (c) {
1463     case '\r':
1464     case '\n':
1465     case 0x2028:
1466     case 0x2029:
1467     done = true;
1468     break;
1469     default:
1470     i++;
1471     }
1472 siliconforks 157 }
1473     size_t line_end = i;
1474 siliconforks 176 if (i < num_characters) {
1475 siliconforks 190 i++;
1476     if (c == '\r' && i < num_characters && characters[i] == '\n') {
1477 siliconforks 157 i++;
1478     }
1479     }
1480 siliconforks 240
1481 siliconforks 190 if (characters_start_with(characters, line_start, line_end, "//#JSCOVERAGE_IF")) {
1482 siliconforks 240 has_conditionals = true;
1483    
1484     if (characters_are_white_space(characters, line_start + 16, line_end)) {
1485     exclusive_directives[line_number - 1] = true;
1486 siliconforks 157 }
1487 siliconforks 240 else {
1488     struct IfDirective * if_directive = xnew(struct IfDirective, 1);
1489     if_directive->condition_start = characters + line_start + 16;
1490     if_directive->condition_end = characters + line_end;
1491     if_directive->start_line = line_number;
1492     if_directive->end_line = 0;
1493     if_directive->next = if_directives;
1494     if_directives = if_directive;
1495 siliconforks 190 }
1496 siliconforks 157 }
1497 siliconforks 190 else if (characters_start_with(characters, line_start, line_end, "//#JSCOVERAGE_ENDIF")) {
1498 siliconforks 240 for (struct IfDirective * p = if_directives; p != NULL; p = p->next) {
1499     if (p->end_line == 0) {
1500     p->end_line = line_number;
1501     break;
1502     }
1503     }
1504 siliconforks 157 }
1505     }
1506    
1507 siliconforks 240 /*
1508     An instrumented JavaScript file has 4 sections:
1509     1. initialization
1510     2. instrumented source code
1511     3. conditionals
1512     4. original source code
1513     */
1514    
1515     Stream * instrumented = Stream_new(0);
1516     instrument_statement(node, instrumented, 0, false);
1517    
1518     /* write line number info to the output */
1519 siliconforks 444 Stream_write_string(output, JSCOVERAGE_INSTRUMENTED_HEADER);
1520 siliconforks 431 switch (jscoverage_mode) {
1521     case JSCOVERAGE_MOZILLA:
1522 siliconforks 361 Stream_write_string(output, "try {\n");
1523 siliconforks 500 Stream_write_string(output, " Components.utils.import('resource://app/modules/jscoverage.jsm');\n");
1524 siliconforks 361 Stream_printf(output, " dump('%s: successfully imported jscoverage module\\n');\n", id);
1525     Stream_write_string(output, "}\n");
1526     Stream_write_string(output, "catch (e) {\n");
1527     Stream_write_string(output, " _$jscoverage = {};\n");
1528     Stream_printf(output, " dump('%s: failed to import jscoverage module - coverage not available for this file\\n');\n", id);
1529     Stream_write_string(output, "}\n");
1530 siliconforks 431 break;
1531     case JSCOVERAGE_NORMAL:
1532 siliconforks 494 /*
1533     // pseudo-code:
1534     if (top && top.opener && top.opener._$jscoverage) {
1535     var _$jscoverage = top._$jscoverage = top.opener._$jscoverage;
1536     }
1537     else if (top && top.opener) {
1538     var _$jscoverage = top._$jscoverage = top.opener._$jscoverage = {};
1539     }
1540     else if (top && top._$jscoverage) {
1541     var _$jscoverage = top._$jscoverage;
1542     }
1543     else if (top) {
1544     var _$jscoverage = top._$jscoverage = {};
1545     }
1546     else if (_$jscoverage) {
1547     // nothing to do!
1548     }
1549     else {
1550     var _$jscoverage = {};
1551     }
1552     */
1553 siliconforks 499 {
1554     const struct Resource * resource = get_resource("header.js");
1555     Stream_write(output, resource->data, resource->length);
1556     }
1557 siliconforks 431 break;
1558     case JSCOVERAGE_NO_BROWSER:
1559     Stream_write_string(output, "if (typeof _$jscoverage === 'undefined') {\n var _$jscoverage = {};\n}\n");
1560     break;
1561 siliconforks 361 }
1562 siliconforks 240 Stream_printf(output, "if (! _$jscoverage['%s']) {\n", file_id);
1563     Stream_printf(output, " _$jscoverage['%s'] = [];\n", file_id);
1564 siliconforks 473 for (uint32_t i = 0; i < num_lines; i++) {
1565 siliconforks 240 if (lines[i]) {
1566     Stream_printf(output, " _$jscoverage['%s'][%d] = 0;\n", file_id, i + 1);
1567     }
1568     }
1569     Stream_write_string(output, "}\n");
1570     free(lines);
1571     lines = NULL;
1572     free(exclusive_directives);
1573     exclusive_directives = NULL;
1574    
1575 siliconforks 434 /* copy the original source to the output */
1576     Stream_printf(output, "_$jscoverage['%s'].source = ", file_id);
1577     jscoverage_write_source(id, characters, num_characters, output);
1578     Stream_printf(output, ";\n");
1579    
1580 siliconforks 240 /* conditionals */
1581     if (has_conditionals) {
1582     Stream_printf(output, "_$jscoverage['%s'].conditionals = [];\n", file_id);
1583     }
1584    
1585     /* copy the instrumented source code to the output */
1586     Stream_write(output, instrumented->data, instrumented->length);
1587    
1588     /* conditionals */
1589     for (struct IfDirective * if_directive = if_directives; if_directive != NULL; if_directive = if_directive->next) {
1590     Stream_write_string(output, "if (!(");
1591 siliconforks 246 print_javascript(if_directive->condition_start, if_directive->condition_end - if_directive->condition_start, output);
1592 siliconforks 240 Stream_write_string(output, ")) {\n");
1593     Stream_printf(output, " _$jscoverage['%s'].conditionals[%d] = %d;\n", file_id, if_directive->start_line, if_directive->end_line);
1594     Stream_write_string(output, "}\n");
1595     }
1596    
1597     /* free */
1598     while (if_directives != NULL) {
1599     struct IfDirective * if_directive = if_directives;
1600     if_directives = if_directives->next;
1601     free(if_directive);
1602     }
1603    
1604 siliconforks 179 Stream_delete(instrumented);
1605    
1606     file_id = NULL;
1607     }
1608    
1609     void jscoverage_write_source(const char * id, const jschar * characters, size_t num_characters, Stream * output) {
1610     Stream_write_string(output, "[");
1611     if (jscoverage_highlight) {
1612     Stream * highlighted_stream = Stream_new(num_characters);
1613     jscoverage_highlight_js(context, id, characters, num_characters, highlighted_stream);
1614     size_t i = 0;
1615     while (i < highlighted_stream->length) {
1616     if (i > 0) {
1617     Stream_write_char(output, ',');
1618     }
1619    
1620     Stream_write_char(output, '"');
1621     bool done = false;
1622     while (! done) {
1623     char c = highlighted_stream->data[i];
1624     switch (c) {
1625     case 0x8:
1626     /* backspace */
1627     Stream_write_string(output, "\\b");
1628     break;
1629     case 0x9:
1630     /* horizontal tab */
1631     Stream_write_string(output, "\\t");
1632     break;
1633     case 0xa:
1634     /* line feed (new line) */
1635     done = true;
1636     break;
1637 siliconforks 317 /* IE doesn't support this */
1638     /*
1639 siliconforks 179 case 0xb:
1640     Stream_write_string(output, "\\v");
1641     break;
1642 siliconforks 317 */
1643 siliconforks 179 case 0xc:
1644     /* form feed */
1645     Stream_write_string(output, "\\f");
1646     break;
1647     case 0xd:
1648     /* carriage return */
1649     done = true;
1650     if (i + 1 < highlighted_stream->length && highlighted_stream->data[i + 1] == '\n') {
1651     i++;
1652     }
1653     break;
1654     case '"':
1655     Stream_write_string(output, "\\\"");
1656     break;
1657     case '\\':
1658     Stream_write_string(output, "\\\\");
1659     break;
1660     default:
1661     Stream_write_char(output, c);
1662     break;
1663     }
1664 siliconforks 95 i++;
1665 siliconforks 179 if (i >= highlighted_stream->length) {
1666     done = true;
1667 siliconforks 95 }
1668     }
1669 siliconforks 179 Stream_write_char(output, '"');
1670     }
1671     Stream_delete(highlighted_stream);
1672     }
1673     else {
1674     size_t i = 0;
1675     while (i < num_characters) {
1676     if (i > 0) {
1677     Stream_write_char(output, ',');
1678     }
1679    
1680     Stream_write_char(output, '"');
1681     bool done = false;
1682     while (! done) {
1683     jschar c = characters[i];
1684     switch (c) {
1685     case 0x8:
1686     /* backspace */
1687     Stream_write_string(output, "\\b");
1688     break;
1689     case 0x9:
1690     /* horizontal tab */
1691     Stream_write_string(output, "\\t");
1692     break;
1693     case 0xa:
1694     /* line feed (new line) */
1695     done = true;
1696     break;
1697 siliconforks 317 /* IE doesn't support this */
1698     /*
1699 siliconforks 179 case 0xb:
1700     Stream_write_string(output, "\\v");
1701     break;
1702 siliconforks 317 */
1703 siliconforks 179 case 0xc:
1704     /* form feed */
1705     Stream_write_string(output, "\\f");
1706     break;
1707     case 0xd:
1708     /* carriage return */
1709     done = true;
1710     if (i + 1 < num_characters && characters[i + 1] == '\n') {
1711     i++;
1712     }
1713     break;
1714     case '"':
1715     Stream_write_string(output, "\\\"");
1716     break;
1717     case '\\':
1718     Stream_write_string(output, "\\\\");
1719     break;
1720     case '&':
1721     Stream_write_string(output, "&amp;");
1722     break;
1723     case '<':
1724     Stream_write_string(output, "&lt;");
1725     break;
1726     case '>':
1727     Stream_write_string(output, "&gt;");
1728     break;
1729     case 0x2028:
1730     case 0x2029:
1731     done = true;
1732     break;
1733     default:
1734     if (32 <= c && c <= 126) {
1735     Stream_write_char(output, c);
1736     }
1737     else {
1738     Stream_printf(output, "&#%d;", c);
1739     }
1740     break;
1741     }
1742 siliconforks 95 i++;
1743 siliconforks 179 if (i >= num_characters) {
1744     done = true;
1745     }
1746 siliconforks 95 }
1747 siliconforks 179 Stream_write_char(output, '"');
1748 siliconforks 95 }
1749     }
1750 siliconforks 179 Stream_write_string(output, "]");
1751 siliconforks 2 }
1752 siliconforks 116
1753     void jscoverage_copy_resources(const char * destination_directory) {
1754     copy_resource("jscoverage.html", destination_directory);
1755     copy_resource("jscoverage.css", destination_directory);
1756     copy_resource("jscoverage.js", destination_directory);
1757 siliconforks 169 copy_resource("jscoverage-ie.css", destination_directory);
1758 siliconforks 116 copy_resource("jscoverage-throbber.gif", destination_directory);
1759 siliconforks 179 copy_resource("jscoverage-highlight.css", destination_directory);
1760 siliconforks 116 }
1761    
1762     /*
1763     coverage reports
1764     */
1765    
1766     struct FileCoverageList {
1767     FileCoverage * file_coverage;
1768     struct FileCoverageList * next;
1769     };
1770    
1771     struct Coverage {
1772     JSHashTable * coverage_table;
1773     struct FileCoverageList * coverage_list;
1774     };
1775    
1776     static int compare_strings(const void * p1, const void * p2) {
1777 siliconforks 399 return strcmp((const char *) p1, (const char *) p2) == 0;
1778 siliconforks 116 }
1779    
1780     Coverage * Coverage_new(void) {
1781 siliconforks 399 Coverage * result = (Coverage *) xmalloc(sizeof(Coverage));
1782 siliconforks 116 result->coverage_table = JS_NewHashTable(1024, JS_HashString, compare_strings, NULL, NULL, NULL);
1783     if (result->coverage_table == NULL) {
1784     fatal("cannot create hash table");
1785     }
1786     result->coverage_list = NULL;
1787     return result;
1788     }
1789    
1790     void Coverage_delete(Coverage * coverage) {
1791     JS_HashTableDestroy(coverage->coverage_table);
1792     struct FileCoverageList * p = coverage->coverage_list;
1793     while (p != NULL) {
1794 siliconforks 179 free(p->file_coverage->coverage_lines);
1795     if (p->file_coverage->source_lines != NULL) {
1796     for (uint32 i = 0; i < p->file_coverage->num_source_lines; i++) {
1797     free(p->file_coverage->source_lines[i]);
1798     }
1799     free(p->file_coverage->source_lines);
1800     }
1801 siliconforks 116 free(p->file_coverage->id);
1802     free(p->file_coverage);
1803     struct FileCoverageList * q = p;
1804     p = p->next;
1805     free(q);
1806     }
1807     free(coverage);
1808     }
1809    
1810     struct EnumeratorArg {
1811     CoverageForeachFunction f;
1812     void * p;
1813     };
1814    
1815     static intN enumerator(JSHashEntry * entry, intN i, void * arg) {
1816 siliconforks 399 struct EnumeratorArg * enumerator_arg = (struct EnumeratorArg *) arg;
1817     enumerator_arg->f((FileCoverage *) entry->value, i, enumerator_arg->p);
1818 siliconforks 116 return 0;
1819     }
1820    
1821     void Coverage_foreach_file(Coverage * coverage, CoverageForeachFunction f, void * p) {
1822     struct EnumeratorArg enumerator_arg;
1823     enumerator_arg.f = f;
1824     enumerator_arg.p = p;
1825     JS_HashTableEnumerateEntries(coverage->coverage_table, enumerator, &enumerator_arg);
1826     }
1827    
1828     int jscoverage_parse_json(Coverage * coverage, const uint8_t * json, size_t length) {
1829 siliconforks 336 int result = 0;
1830    
1831 siliconforks 116 jschar * base = js_InflateString(context, (char *) json, &length);
1832     if (base == NULL) {
1833     fatal("out of memory");
1834     }
1835    
1836     jschar * parenthesized_json = xnew(jschar, addst(length, 2));
1837     parenthesized_json[0] = '(';
1838     memcpy(parenthesized_json + 1, base, mulst(length, sizeof(jschar)));
1839     parenthesized_json[length + 1] = ')';
1840    
1841     JS_free(context, base);
1842    
1843 siliconforks 460 JSCompiler compiler(context);
1844     if (! compiler.init(parenthesized_json, length + 2, NULL, NULL, 1)) {
1845 siliconforks 336 free(parenthesized_json);
1846     return -1;
1847 siliconforks 116 }
1848 siliconforks 460 JSParseNode * root = compiler.parse(global);
1849 siliconforks 116 free(parenthesized_json);
1850 siliconforks 399
1851     JSParseNode * semi = NULL;
1852     JSParseNode * object = NULL;
1853    
1854 siliconforks 116 if (root == NULL) {
1855 siliconforks 336 result = -1;
1856     goto done;
1857 siliconforks 116 }
1858    
1859     /* root node must be TOK_LC */
1860     if (root->pn_type != TOK_LC) {
1861 siliconforks 336 result = -1;
1862     goto done;
1863 siliconforks 116 }
1864 siliconforks 399 semi = root->pn_u.list.head;
1865 siliconforks 116
1866     /* the list must be TOK_SEMI and it must contain only one element */
1867     if (semi->pn_type != TOK_SEMI || semi->pn_next != NULL) {
1868 siliconforks 336 result = -1;
1869     goto done;
1870 siliconforks 116 }
1871 siliconforks 399 object = semi->pn_kid;
1872 siliconforks 116
1873     /* this must be an object literal */
1874     if (object->pn_type != TOK_RC) {
1875 siliconforks 336 result = -1;
1876     goto done;
1877 siliconforks 116 }
1878    
1879     for (JSParseNode * p = object->pn_head; p != NULL; p = p->pn_next) {
1880     /* every element of this list must be TOK_COLON */
1881     if (p->pn_type != TOK_COLON) {
1882 siliconforks 336 result = -1;
1883     goto done;
1884 siliconforks 116 }
1885    
1886     /* the key must be a string representing the file */
1887     JSParseNode * key = p->pn_left;
1888     if (key->pn_type != TOK_STRING || ! ATOM_IS_STRING(key->pn_atom)) {
1889 siliconforks 336 result = -1;
1890     goto done;
1891 siliconforks 116 }
1892     char * id_bytes = JS_GetStringBytes(ATOM_TO_STRING(key->pn_atom));
1893    
1894     /* the value must be an object literal OR an array */
1895     JSParseNode * value = p->pn_right;
1896     if (! (value->pn_type == TOK_RC || value->pn_type == TOK_RB)) {
1897 siliconforks 336 result = -1;
1898     goto done;
1899 siliconforks 116 }
1900    
1901     JSParseNode * array = NULL;
1902     JSParseNode * source = NULL;
1903     if (value->pn_type == TOK_RB) {
1904     /* an array */
1905     array = value;
1906     }
1907     else if (value->pn_type == TOK_RC) {
1908     /* an object literal */
1909     if (value->pn_count != 2) {
1910 siliconforks 336 result = -1;
1911     goto done;
1912 siliconforks 116 }
1913     for (JSParseNode * element = value->pn_head; element != NULL; element = element->pn_next) {
1914     if (element->pn_type != TOK_COLON) {
1915 siliconforks 336 result = -1;
1916     goto done;
1917 siliconforks 116 }
1918     JSParseNode * left = element->pn_left;
1919     if (left->pn_type != TOK_STRING || ! ATOM_IS_STRING(left->pn_atom)) {
1920 siliconforks 336 result = -1;
1921     goto done;
1922 siliconforks 116 }
1923     const char * s = JS_GetStringBytes(ATOM_TO_STRING(left->pn_atom));
1924     if (strcmp(s, "coverage") == 0) {
1925     array = element->pn_right;
1926     if (array->pn_type != TOK_RB) {
1927 siliconforks 336 result = -1;
1928     goto done;
1929 siliconforks 116 }
1930     }
1931     else if (strcmp(s, "source") == 0) {
1932     source = element->pn_right;
1933 siliconforks 179 if (source->pn_type != TOK_RB) {
1934 siliconforks 336 result = -1;
1935     goto done;
1936 siliconforks 116 }
1937     }
1938     else {
1939 siliconforks 336 result = -1;
1940     goto done;
1941 siliconforks 116 }
1942     }
1943     }
1944     else {
1945 siliconforks 336 result = -1;
1946     goto done;
1947 siliconforks 116 }
1948    
1949     if (array == NULL) {
1950 siliconforks 336 result = -1;
1951     goto done;
1952 siliconforks 116 }
1953    
1954     /* look up the file in the coverage table */
1955 siliconforks 399 FileCoverage * file_coverage = (FileCoverage *) JS_HashTableLookup(coverage->coverage_table, id_bytes);
1956 siliconforks 116 if (file_coverage == NULL) {
1957     /* not there: create a new one */
1958     char * id = xstrdup(id_bytes);
1959 siliconforks 399 file_coverage = (FileCoverage *) xmalloc(sizeof(FileCoverage));
1960 siliconforks 116 file_coverage->id = id;
1961 siliconforks 179 file_coverage->num_coverage_lines = array->pn_count;
1962     file_coverage->coverage_lines = xnew(int, array->pn_count);
1963 siliconforks 245 file_coverage->source_lines = NULL;
1964 siliconforks 116
1965     /* set coverage for all lines */
1966     uint32 i = 0;
1967     for (JSParseNode * element = array->pn_head; element != NULL; element = element->pn_next, i++) {
1968     if (element->pn_type == TOK_NUMBER) {
1969 siliconforks 179 file_coverage->coverage_lines[i] = (int) element->pn_dval;
1970 siliconforks 116 }
1971     else if (element->pn_type == TOK_PRIMARY && element->pn_op == JSOP_NULL) {
1972 siliconforks 179 file_coverage->coverage_lines[i] = -1;
1973 siliconforks 116 }
1974     else {
1975 siliconforks 336 result = -1;
1976     goto done;
1977 siliconforks 116 }
1978     }
1979     assert(i == array->pn_count);
1980    
1981     /* add to the hash table */
1982     JS_HashTableAdd(coverage->coverage_table, id, file_coverage);
1983 siliconforks 399 struct FileCoverageList * coverage_list = (FileCoverageList *) xmalloc(sizeof(struct FileCoverageList));
1984 siliconforks 116 coverage_list->file_coverage = file_coverage;
1985     coverage_list->next = coverage->coverage_list;
1986     coverage->coverage_list = coverage_list;
1987     }
1988     else {
1989     /* sanity check */
1990     assert(strcmp(file_coverage->id, id_bytes) == 0);
1991 siliconforks 179 if (file_coverage->num_coverage_lines != array->pn_count) {
1992 siliconforks 336 result = -2;
1993     goto done;
1994 siliconforks 116 }
1995    
1996     /* merge the coverage */
1997     uint32 i = 0;
1998     for (JSParseNode * element = array->pn_head; element != NULL; element = element->pn_next, i++) {
1999     if (element->pn_type == TOK_NUMBER) {
2000 siliconforks 179 if (file_coverage->coverage_lines[i] == -1) {
2001 siliconforks 336 result = -2;
2002     goto done;
2003 siliconforks 116 }
2004 siliconforks 179 file_coverage->coverage_lines[i] += (int) element->pn_dval;
2005 siliconforks 116 }
2006     else if (element->pn_type == TOK_PRIMARY && element->pn_op == JSOP_NULL) {
2007 siliconforks 179 if (file_coverage->coverage_lines[i] != -1) {
2008 siliconforks 336 result = -2;
2009     goto done;
2010 siliconforks 116 }
2011     }
2012     else {
2013 siliconforks 336 result = -1;
2014     goto done;
2015 siliconforks 116 }
2016     }
2017     assert(i == array->pn_count);
2018 siliconforks 245 }
2019 siliconforks 116
2020 siliconforks 245 /* if this JSON file has source, use it */
2021     if (file_coverage->source_lines == NULL && source != NULL) {
2022     file_coverage->num_source_lines = source->pn_count;
2023     file_coverage->source_lines = xnew(char *, source->pn_count);
2024     uint32 i = 0;
2025     for (JSParseNode * element = source->pn_head; element != NULL; element = element->pn_next, i++) {
2026     if (element->pn_type != TOK_STRING) {
2027 siliconforks 336 result = -1;
2028     goto done;
2029 siliconforks 179 }
2030 siliconforks 245 file_coverage->source_lines[i] = xstrdup(JS_GetStringBytes(ATOM_TO_STRING(element->pn_atom)));
2031 siliconforks 116 }
2032 siliconforks 245 assert(i == source->pn_count);
2033 siliconforks 116 }
2034     }
2035    
2036 siliconforks 336 done:
2037     return result;
2038 siliconforks 116 }

Properties

Name Value
svn:mergeinfo

  ViewVC Help
Powered by ViewVC 1.1.24