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

Annotation of /trunk/instrument-js.cpp

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC 1.1.24