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

Annotation of /trunk/instrument-js.c

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC 1.1.24