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

Diff of /trunk/instrument-js.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 87 by siliconforks, Mon May 5 20:05:27 2008 UTC revision 214 by siliconforks, Fri Oct 3 02:26:04 2008 UTC
# Line 17  Line 17 
17      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  */  */
19    
20    #include <config.h>
21    
22  #include "instrument-js.h"  #include "instrument-js.h"
23    
24  #include <assert.h>  #include <assert.h>
# Line 32  Line 34 
34  #include <jsscope.h>  #include <jsscope.h>
35  #include <jsstr.h>  #include <jsstr.h>
36    
37    #include "encoding.h"
38    #include "global.h"
39    #include "highlight.h"
40    #include "resource-manager.h"
41  #include "util.h"  #include "util.h"
42    
43  static JSRuntime * runtime = NULL;  static JSRuntime * runtime = NULL;
# Line 44  Line 50 
50  */  */
51  static const char * file_id = NULL;  static const char * file_id = NULL;
52  static char * lines = NULL;  static char * lines = NULL;
53    static uint16_t num_lines = 0;
54    
55  void jscoverage_init(void) {  void jscoverage_init(void) {
56    runtime = JS_NewRuntime(8L * 1024L * 1024L);    runtime = JS_NewRuntime(8L * 1024L * 1024L);
# Line 71  Line 78 
78    JS_DestroyRuntime(runtime);    JS_DestroyRuntime(runtime);
79  }  }
80    
81  static void print_string(JSString * s, FILE * f) {  static void print_string(JSString * s, Stream * f) {
82    for (int i = 0; i < s->length; i++) {    size_t length = JSSTRING_LENGTH(s);
83      char c = s->chars[i];    jschar * characters = JSSTRING_CHARS(s);
84      fputc(c, f);    for (size_t i = 0; i < length; i++) {
85        jschar c = characters[i];
86        if (32 <= c && c <= 126) {
87          switch (c) {
88          case '"':
89            Stream_write_string(f, "\\\"");
90            break;
91    /*
92          case '\'':
93            Stream_write_string(f, "\\'");
94            break;
95    */
96          case '\\':
97            Stream_write_string(f, "\\\\");
98            break;
99          default:
100            Stream_write_char(f, c);
101            break;
102          }
103        }
104        else {
105          switch (c) {
106          case 0x8:
107            Stream_write_string(f, "\\b");
108            break;
109          case 0x9:
110            Stream_write_string(f, "\\t");
111            break;
112          case 0xa:
113            Stream_write_string(f, "\\n");
114            break;
115          case 0xb:
116            Stream_write_string(f, "\\v");
117            break;
118          case 0xc:
119            Stream_write_string(f, "\\f");
120            break;
121          case 0xd:
122            Stream_write_string(f, "\\r");
123            break;
124          default:
125            Stream_printf(f, "\\u%04x", c);
126            break;
127          }
128        }
129    }    }
130  }  }
131    
132  static void print_string_atom(JSAtom * atom, FILE * f) {  static void print_string_atom(JSAtom * atom, Stream * f) {
133    assert(ATOM_IS_STRING(atom));    assert(ATOM_IS_STRING(atom));
134    JSString * s = ATOM_TO_STRING(atom);    JSString * s = ATOM_TO_STRING(atom);
135    print_string(s, f);    print_string(s, f);
136  }  }
137    
138  static void print_string_jsval(jsval value, FILE * f) {  static void print_regex(jsval value, Stream * f) {
139    assert(JSVAL_IS_STRING(value));    assert(JSVAL_IS_STRING(value));
140    JSString * s = JSVAL_TO_STRING(value);    JSString * s = JSVAL_TO_STRING(value);
141    print_string(s, f);    size_t length = JSSTRING_LENGTH(s);
142      jschar * characters = JSSTRING_CHARS(s);
143      for (size_t i = 0; i < length; i++) {
144        jschar c = characters[i];
145        if (32 <= c && c <= 126) {
146          Stream_write_char(f, c);
147        }
148        else {
149          Stream_printf(f, "\\u%04x", c);
150        }
151      }
152  }  }
153    
154  static void print_quoted_string_atom(JSAtom * atom, FILE * f) {  static void print_quoted_string_atom(JSAtom * atom, Stream * f) {
155    assert(ATOM_IS_STRING(atom));    assert(ATOM_IS_STRING(atom));
156    JSString * s = ATOM_TO_STRING(atom);    JSString * s = ATOM_TO_STRING(atom);
157    JSString * quoted = js_QuoteString(context, s, '"');    Stream_write_char(f, '"');
158    print_string(quoted, f);    print_string(s, f);
159      Stream_write_char(f, '"');
160  }  }
161    
162  static const char * get_op(uint8 op) {  static const char * get_op(uint8 op) {
# Line 142  Line 204 
204    }    }
205  }  }
206    
207  static void instrument_expression(JSParseNode * node, FILE * f);  static void instrument_expression(JSParseNode * node, Stream * f);
208  static void instrument_statement(JSParseNode * node, FILE * f, int indent);  static void instrument_statement(JSParseNode * node, Stream * f, int indent);
209    
210  static void instrument_function(JSParseNode * node, FILE * f, int indent) {  enum FunctionType {
211      assert(node->pn_arity == PN_FUNC);    FUNCTION_NORMAL,
212      assert(ATOM_IS_OBJECT(node->pn_funAtom));    FUNCTION_GETTER_OR_SETTER
213      JSObject * object = ATOM_TO_OBJECT(node->pn_funAtom);  };
214      assert(JS_ObjectIsFunction(context, object));  
215      JSFunction * function = (JSFunction *) JS_GetPrivate(context, object);  static void instrument_function(JSParseNode * node, Stream * f, int indent, enum FunctionType type) {
216      assert(function);    assert(node->pn_arity == PN_FUNC);
217      assert(object == function->object);    assert(ATOM_IS_OBJECT(node->pn_funAtom));
218      fprintf(f, "%*s", indent, "");    JSObject * object = ATOM_TO_OBJECT(node->pn_funAtom);
219      fprintf(f, "function");    assert(JS_ObjectIsFunction(context, object));
220      JSFunction * function = (JSFunction *) JS_GetPrivate(context, object);
221      /* function name */    assert(function);
222      if (function->atom) {    assert(object == function->object);
223        fputc(' ', f);    Stream_printf(f, "%*s", indent, "");
224        print_string_atom(function->atom, f);    if (type == FUNCTION_NORMAL) {
225      }      Stream_write_string(f, "function");
226      }
227      /* function parameters */  
228      fprintf(f, "(");    /* function name */
229      JSAtom ** params = xmalloc(function->nargs * sizeof(JSAtom *));    if (function->atom) {
230      for (int i = 0; i < function->nargs; i++) {      Stream_write_char(f, ' ');
231        /* initialize to NULL for sanity check */      print_string_atom(function->atom, f);
232        params[i] = NULL;    }
233      }  
234      JSScope * scope = OBJ_SCOPE(object);    /* function parameters */
235      for (JSScopeProperty * scope_property = SCOPE_LAST_PROP(scope); scope_property != NULL; scope_property = scope_property->parent) {    Stream_write_string(f, "(");
236        if (scope_property->getter != js_GetArgument) {    JSAtom ** params = xnew(JSAtom *, function->nargs);
237          continue;    for (int i = 0; i < function->nargs; i++) {
238        }      /* initialize to NULL for sanity check */
239        assert(scope_property->flags & SPROP_HAS_SHORTID);      params[i] = NULL;
240        assert((uint16) scope_property->shortid < function->nargs);    }
241        assert(JSID_IS_ATOM(scope_property->id));    JSScope * scope = OBJ_SCOPE(object);
242        params[(uint16) scope_property->shortid] = JSID_TO_ATOM(scope_property->id);    for (JSScopeProperty * scope_property = SCOPE_LAST_PROP(scope); scope_property != NULL; scope_property = scope_property->parent) {
243        if (scope_property->getter != js_GetArgument) {
244          continue;
245        }
246        assert(scope_property->flags & SPROP_HAS_SHORTID);
247        assert((uint16) scope_property->shortid < function->nargs);
248        assert(JSID_IS_ATOM(scope_property->id));
249        params[(uint16) scope_property->shortid] = JSID_TO_ATOM(scope_property->id);
250      }
251      for (int i = 0; i < function->nargs; i++) {
252        assert(params[i] != NULL);
253        if (i > 0) {
254          Stream_write_string(f, ", ");
255      }      }
256      for (int i = 0; i < function->nargs; i++) {      if (ATOM_IS_STRING(params[i])) {
257        assert(params[i] != NULL);        print_string_atom(params[i], f);
       if (i > 0) {  
         fprintf(f, ", ");  
       }  
       if (ATOM_IS_STRING(params[i])) {  
         print_string_atom(params[i], f);  
       }  
258      }      }
259      fprintf(f, ") {\n");    }
260      free(params);    Stream_write_string(f, ") {\n");
261      free(params);
262    
263      /* function body */    /* function body */
264      instrument_statement(node->pn_body, f, indent + 2);    instrument_statement(node->pn_body, f, indent + 2);
265    
266      fprintf(f, "}\n");    Stream_write_string(f, "}\n");
267  }  }
268    
269  static void instrument_function_call(JSParseNode * node, FILE * f) {  static void instrument_function_call(JSParseNode * node, Stream * f) {
270    instrument_expression(node->pn_head, f);    instrument_expression(node->pn_head, f);
271    fputc('(', f);    Stream_write_char(f, '(');
272    for (struct JSParseNode * p = node->pn_head->pn_next; p != NULL; p = p->pn_next) {    for (struct JSParseNode * p = node->pn_head->pn_next; p != NULL; p = p->pn_next) {
273      if (p != node->pn_head->pn_next) {      if (p != node->pn_head->pn_next) {
274        fprintf(f, ", ");        Stream_write_string(f, ", ");
275      }      }
276      instrument_expression(p, f);      instrument_expression(p, f);
277    }    }
278    fputc(')', f);    Stream_write_char(f, ')');
279  }  }
280    
281  /*  /*
# Line 222  Line 291 
291  TOK_INSTANCEOF  binary  TOK_INSTANCEOF  binary
292  TOK_IN          binary  TOK_IN          binary
293  */  */
294  static void instrument_expression(JSParseNode * node, FILE * f) {  static void instrument_expression(JSParseNode * node, Stream * f) {
295    switch (node->pn_type) {    switch (node->pn_type) {
296    case TOK_FUNCTION:    case TOK_FUNCTION:
297      instrument_function(node, f, 0);      instrument_function(node, f, 0, FUNCTION_NORMAL);
298      break;      break;
299    case TOK_COMMA:    case TOK_COMMA:
300      for (struct JSParseNode * p = node->pn_head; p != NULL; p = p->pn_next) {      for (struct JSParseNode * p = node->pn_head; p != NULL; p = p->pn_next) {
301        if (p != node->pn_head) {        if (p != node->pn_head) {
302          fprintf(f, ", ");          Stream_write_string(f, ", ");
303        }        }
304        instrument_expression(p, f);        instrument_expression(p, f);
305      }      }
306      break;      break;
307    case TOK_ASSIGN:    case TOK_ASSIGN:
308      instrument_expression(node->pn_left, f);      instrument_expression(node->pn_left, f);
309      fputc(' ', f);      Stream_write_char(f, ' ');
310      switch (node->pn_op) {      switch (node->pn_op) {
311      case JSOP_ADD:      case JSOP_ADD:
312      case JSOP_SUB:      case JSOP_SUB:
# Line 250  Line 319 
319      case JSOP_BITOR:      case JSOP_BITOR:
320      case JSOP_BITXOR:      case JSOP_BITXOR:
321      case JSOP_DIV:      case JSOP_DIV:
322        fprintf(f, "%s", get_op(node->pn_op));        Stream_printf(f, "%s", get_op(node->pn_op));
323        break;        break;
324      default:      default:
325        /* do nothing - it must be a simple assignment */        /* do nothing - it must be a simple assignment */
326        break;        break;
327      }      }
328      fprintf(f, "= ");      Stream_write_string(f, "= ");
329      instrument_expression(node->pn_right, f);      instrument_expression(node->pn_right, f);
330      break;      break;
331    case TOK_HOOK:    case TOK_HOOK:
332      instrument_expression(node->pn_kid1, f);      instrument_expression(node->pn_kid1, f);
333      fprintf(f, "? ");      Stream_write_string(f, "? ");
334      instrument_expression(node->pn_kid2, f);      instrument_expression(node->pn_kid2, f);
335      fprintf(f, ": ");      Stream_write_string(f, ": ");
336      instrument_expression(node->pn_kid3, f);      instrument_expression(node->pn_kid3, f);
337      break;      break;
338    case TOK_OR:    case TOK_OR:
339      instrument_expression(node->pn_left, f);      instrument_expression(node->pn_left, f);
340      fprintf(f, " || ");      Stream_write_string(f, " || ");
341      instrument_expression(node->pn_right, f);      instrument_expression(node->pn_right, f);
342      break;      break;
343    case TOK_AND:    case TOK_AND:
344      instrument_expression(node->pn_left, f);      instrument_expression(node->pn_left, f);
345      fprintf(f, " && ");      Stream_write_string(f, " && ");
346      instrument_expression(node->pn_right, f);      instrument_expression(node->pn_right, f);
347      break;      break;
348    case TOK_BITOR:    case TOK_BITOR:
# Line 289  Line 358 
358      switch (node->pn_arity) {      switch (node->pn_arity) {
359      case PN_BINARY:      case PN_BINARY:
360        instrument_expression(node->pn_left, f);        instrument_expression(node->pn_left, f);
361        fprintf(f, " %s ", get_op(node->pn_op));        Stream_printf(f, " %s ", get_op(node->pn_op));
362        instrument_expression(node->pn_right, f);        instrument_expression(node->pn_right, f);
363        break;        break;
364      case PN_LIST:      case PN_LIST:
365        for (struct JSParseNode * p = node->pn_head; p != NULL; p = p->pn_next) {        for (struct JSParseNode * p = node->pn_head; p != NULL; p = p->pn_next) {
366          if (p != node->pn_head) {          if (p != node->pn_head) {
367            fprintf(f, " %s ", get_op(node->pn_op));            Stream_printf(f, " %s ", get_op(node->pn_op));
368          }          }
369          instrument_expression(p, f);          instrument_expression(p, f);
370        }        }
# Line 307  Line 376 
376    case TOK_UNARYOP:    case TOK_UNARYOP:
377      switch (node->pn_op) {      switch (node->pn_op) {
378      case JSOP_NEG:      case JSOP_NEG:
379        fputc('-', f);        Stream_write_char(f, '-');
380        instrument_expression(node->pn_kid, f);        instrument_expression(node->pn_kid, f);
381        break;        break;
382      case JSOP_POS:      case JSOP_POS:
383        fputc('+', f);        Stream_write_char(f, '+');
384        instrument_expression(node->pn_kid, f);        instrument_expression(node->pn_kid, f);
385        break;        break;
386      case JSOP_NOT:      case JSOP_NOT:
387        fputc('!', f);        Stream_write_char(f, '!');
388        instrument_expression(node->pn_kid, f);        instrument_expression(node->pn_kid, f);
389        break;        break;
390      case JSOP_BITNOT:      case JSOP_BITNOT:
391        fputc('~', f);        Stream_write_char(f, '~');
392        instrument_expression(node->pn_kid, f);        instrument_expression(node->pn_kid, f);
393        break;        break;
394      case JSOP_TYPEOF:      case JSOP_TYPEOF:
395        fprintf(f, "typeof ");        Stream_write_string(f, "typeof ");
396        instrument_expression(node->pn_kid, f);        instrument_expression(node->pn_kid, f);
397        break;        break;
398      case JSOP_VOID:      case JSOP_VOID:
399        fprintf(f, "void ");        Stream_write_string(f, "void ");
400        instrument_expression(node->pn_kid, f);        instrument_expression(node->pn_kid, f);
401        break;        break;
402      default:      default:
# Line 344  Line 413 
413      case JSOP_INCNAME:      case JSOP_INCNAME:
414      case JSOP_INCPROP:      case JSOP_INCPROP:
415      case JSOP_INCELEM:      case JSOP_INCELEM:
416        fprintf(f, "++");        Stream_write_string(f, "++");
417        instrument_expression(node->pn_kid, f);        instrument_expression(node->pn_kid, f);
418        break;        break;
419      case JSOP_DECNAME:      case JSOP_DECNAME:
420      case JSOP_DECPROP:      case JSOP_DECPROP:
421      case JSOP_DECELEM:      case JSOP_DECELEM:
422        fprintf(f, "--");        Stream_write_string(f, "--");
423        instrument_expression(node->pn_kid, f);        instrument_expression(node->pn_kid, f);
424        break;        break;
425      case JSOP_NAMEINC:      case JSOP_NAMEINC:
426      case JSOP_PROPINC:      case JSOP_PROPINC:
427      case JSOP_ELEMINC:      case JSOP_ELEMINC:
428        instrument_expression(node->pn_kid, f);        instrument_expression(node->pn_kid, f);
429        fprintf(f, "++");        Stream_write_string(f, "++");
430        break;        break;
431      case JSOP_NAMEDEC:      case JSOP_NAMEDEC:
432      case JSOP_PROPDEC:      case JSOP_PROPDEC:
433      case JSOP_ELEMDEC:      case JSOP_ELEMDEC:
434        instrument_expression(node->pn_kid, f);        instrument_expression(node->pn_kid, f);
435        fprintf(f, "--");        Stream_write_string(f, "--");
436        break;        break;
437      default:      default:
438        abort();        abort();
# Line 371  Line 440 
440      }      }
441      break;      break;
442    case TOK_NEW:    case TOK_NEW:
443      fprintf(f, "new ");      Stream_write_string(f, "new ");
444      instrument_function_call(node, f);      instrument_function_call(node, f);
445      break;      break;
446    case TOK_DELETE:    case TOK_DELETE:
447      fprintf(f, "delete ");      Stream_write_string(f, "delete ");
448      instrument_expression(node->pn_kid, f);      instrument_expression(node->pn_kid, f);
449      break;      break;
450    case TOK_DOT:    case TOK_DOT:
# Line 390  Line 459 
459        JSString * s = ATOM_TO_STRING(node->pn_atom);        JSString * s = ATOM_TO_STRING(node->pn_atom);
460        /* XXX - semantics changed in 1.7 */        /* XXX - semantics changed in 1.7 */
461        if (! ATOM_KEYWORD(node->pn_atom) && js_IsIdentifier(s)) {        if (! ATOM_KEYWORD(node->pn_atom) && js_IsIdentifier(s)) {
462          fputc('.', f);          Stream_write_char(f, '.');
463          print_string_atom(node->pn_atom, f);          print_string_atom(node->pn_atom, f);
464        }        }
465        else {        else {
466          fputc('[', f);          Stream_write_char(f, '[');
467          print_quoted_string_atom(node->pn_atom, f);          print_quoted_string_atom(node->pn_atom, f);
468          fputc(']', f);          Stream_write_char(f, ']');
469        }        }
470      }      }
471      break;      break;
472    case TOK_LB:    case TOK_LB:
473      instrument_expression(node->pn_left, f);      instrument_expression(node->pn_left, f);
474      fputc('[', f);      Stream_write_char(f, '[');
475      instrument_expression(node->pn_right, f);      instrument_expression(node->pn_right, f);
476      fputc(']', f);      Stream_write_char(f, ']');
477      break;      break;
478    case TOK_LP:    case TOK_LP:
479      instrument_function_call(node, f);      instrument_function_call(node, f);
480      break;      break;
481    case TOK_RB:    case TOK_RB:
482      fputc('[', f);      Stream_write_char(f, '[');
483      for (struct JSParseNode * p = node->pn_head; p != NULL; p = p->pn_next) {      for (struct JSParseNode * p = node->pn_head; p != NULL; p = p->pn_next) {
484        if (p != node->pn_head) {        if (p != node->pn_head) {
485          fprintf(f, ", ");          Stream_write_string(f, ", ");
486        }        }
487        /* TOK_COMMA is a special case: a hole in the array */        /* TOK_COMMA is a special case: a hole in the array */
488        if (p->pn_type != TOK_COMMA) {        if (p->pn_type != TOK_COMMA) {
# Line 421  Line 490 
490        }        }
491      }      }
492      if (node->pn_extra == PNX_ENDCOMMA) {      if (node->pn_extra == PNX_ENDCOMMA) {
493        fputc(',', f);        Stream_write_char(f, ',');
494      }      }
495      fputc(']', f);      Stream_write_char(f, ']');
496      break;      break;
497    case TOK_RC:    case TOK_RC:
498      fputc('{', f);      Stream_write_char(f, '{');
499      for (struct JSParseNode * p = node->pn_head; p != NULL; p = p->pn_next) {      for (struct JSParseNode * p = node->pn_head; p != NULL; p = p->pn_next) {
500        assert(p->pn_type == TOK_COLON);        assert(p->pn_type == TOK_COLON);
501        if (p != node->pn_head) {        if (p != node->pn_head) {
502          fprintf(f, ", ");          Stream_write_string(f, ", ");
503          }
504    
505          /* check whether this is a getter or setter */
506          switch (p->pn_op) {
507          case JSOP_GETTER:
508            Stream_write_string(f, "get ");
509            instrument_expression(p->pn_left, f);
510            instrument_function(p->pn_right, f, 0, FUNCTION_GETTER_OR_SETTER);
511            break;
512          case JSOP_SETTER:
513            Stream_write_string(f, "set ");
514            instrument_expression(p->pn_left, f);
515            instrument_function(p->pn_right, f, 0, FUNCTION_GETTER_OR_SETTER);
516            break;
517          default:
518            instrument_expression(p->pn_left, f);
519            Stream_write_string(f, ": ");
520            instrument_expression(p->pn_right, f);
521            break;
522        }        }
       instrument_expression(p->pn_left, f);  
       fprintf(f, ": ");  
       instrument_expression(p->pn_right, f);  
523      }      }
524      fputc('}', f);      Stream_write_char(f, '}');
525      break;      break;
526    case TOK_RP:    case TOK_RP:
527      fputc('(', f);      Stream_write_char(f, '(');
528      instrument_expression(node->pn_kid, f);      instrument_expression(node->pn_kid, f);
529      fputc(')', f);      Stream_write_char(f, ')');
530      break;      break;
531    case TOK_NAME:    case TOK_NAME:
532      print_string_atom(node->pn_atom, f);      print_string_atom(node->pn_atom, f);
# Line 461  Line 546 
546          JSObject * object = ATOM_TO_OBJECT(node->pn_atom);          JSObject * object = ATOM_TO_OBJECT(node->pn_atom);
547          jsval result;          jsval result;
548          js_regexp_toString(context, object, 0, NULL, &result);          js_regexp_toString(context, object, 0, NULL, &result);
549          print_string_jsval(result, f);          print_regex(result, f);
550        }        }
551        break;        break;
552      default:      default:
# Line 477  Line 562 
562      To keep the output simple, special-case zero.      To keep the output simple, special-case zero.
563      */      */
564      if (node->pn_dval == 0.0) {      if (node->pn_dval == 0.0) {
565        fprintf(f, "0");        Stream_write_string(f, "0");
566      }      }
567      else {      else {
568        fprintf(f, "%.15g", node->pn_dval);        Stream_printf(f, "%.15g", node->pn_dval);
569      }      }
570      break;      break;
571    case TOK_PRIMARY:    case TOK_PRIMARY:
572      switch (node->pn_op) {      switch (node->pn_op) {
573      case JSOP_TRUE:      case JSOP_TRUE:
574        fprintf(f, "true");        Stream_write_string(f, "true");
575        break;        break;
576      case JSOP_FALSE:      case JSOP_FALSE:
577        fprintf(f, "false");        Stream_write_string(f, "false");
578        break;        break;
579      case JSOP_NULL:      case JSOP_NULL:
580        fprintf(f, "null");        Stream_write_string(f, "null");
581        break;        break;
582      case JSOP_THIS:      case JSOP_THIS:
583        fprintf(f, "this");        Stream_write_string(f, "this");
584        break;        break;
585      /* jsscan.h mentions `super' ??? */      /* jsscan.h mentions `super' ??? */
586      default:      default:
# Line 504  Line 589 
589      break;      break;
590    case TOK_INSTANCEOF:    case TOK_INSTANCEOF:
591      instrument_expression(node->pn_left, f);      instrument_expression(node->pn_left, f);
592      fprintf(f, " instanceof ");      Stream_write_string(f, " instanceof ");
593      instrument_expression(node->pn_right, f);      instrument_expression(node->pn_right, f);
594      break;      break;
595    case TOK_IN:    case TOK_IN:
596      instrument_expression(node->pn_left, f);      instrument_expression(node->pn_left, f);
597      fprintf(f, " in ");      Stream_write_string(f, " in ");
598      instrument_expression(node->pn_right, f);      instrument_expression(node->pn_right, f);
599      break;      break;
600    default:    default:
# Line 517  Line 602 
602    }    }
603  }  }
604    
605  static void instrument_var_statement(JSParseNode * node, FILE * f, int indent) {  static void instrument_var_statement(JSParseNode * node, Stream * f, int indent) {
606    assert(node->pn_arity == PN_LIST);    assert(node->pn_arity == PN_LIST);
607    fprintf(f, "%*s", indent, "");    Stream_printf(f, "%*s", indent, "");
608    fprintf(f, "var ");    Stream_write_string(f, "var ");
609    for (struct JSParseNode * p = node->pn_u.list.head; p != NULL; p = p->pn_next) {    for (struct JSParseNode * p = node->pn_u.list.head; p != NULL; p = p->pn_next) {
610      assert(p->pn_type == TOK_NAME);      assert(p->pn_type == TOK_NAME);
611      assert(p->pn_arity == PN_NAME);      assert(p->pn_arity == PN_NAME);
612      if (p != node->pn_head) {      if (p != node->pn_head) {
613        fprintf(f, ", ");        Stream_write_string(f, ", ");
614      }      }
615      print_string_atom(p->pn_atom, f);      print_string_atom(p->pn_atom, f);
616      if (p->pn_expr != NULL) {      if (p->pn_expr != NULL) {
617        fprintf(f, " = ");        Stream_write_string(f, " = ");
618        instrument_expression(p->pn_expr, f);        instrument_expression(p->pn_expr, f);
619      }      }
620    }    }
621  }  }
622    
623  static void output_statement(JSParseNode * node, FILE * f, int indent) {  static void output_statement(JSParseNode * node, Stream * f, int indent) {
624    switch (node->pn_type) {    switch (node->pn_type) {
625    case TOK_FUNCTION:    case TOK_FUNCTION:
626      instrument_function(node, f, indent);      instrument_function(node, f, indent, FUNCTION_NORMAL);
627      break;      break;
628    case TOK_LC:    case TOK_LC:
629      assert(node->pn_arity == PN_LIST);      assert(node->pn_arity == PN_LIST);
630  /*  /*
631      fprintf(f, "{\n");      Stream_write_string(f, "{\n");
632  */  */
633      for (struct JSParseNode * p = node->pn_u.list.head; p != NULL; p = p->pn_next) {      for (struct JSParseNode * p = node->pn_u.list.head; p != NULL; p = p->pn_next) {
634        instrument_statement(p, f, indent);        instrument_statement(p, f, indent);
635      }      }
636  /*  /*
637      fprintf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
638      fprintf(f, "}\n");      Stream_write_string(f, "}\n");
639  */  */
640      break;      break;
641    case TOK_IF:    case TOK_IF:
642      assert(node->pn_arity == PN_TERNARY);      assert(node->pn_arity == PN_TERNARY);
643      fprintf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
644      fprintf(f, "if (");      Stream_write_string(f, "if (");
645      instrument_expression(node->pn_kid1, f);      instrument_expression(node->pn_kid1, f);
646      fprintf(f, ") {\n");      Stream_write_string(f, ") {\n");
647      instrument_statement(node->pn_kid2, f, indent + 2);      instrument_statement(node->pn_kid2, f, indent + 2);
648      fprintf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
649      fprintf(f, "}\n");      Stream_write_string(f, "}\n");
650      if (node->pn_kid3) {      if (node->pn_kid3) {
651        fprintf(f, "%*s", indent, "");        Stream_printf(f, "%*s", indent, "");
652        fprintf(f, "else {\n");        Stream_write_string(f, "else {\n");
653        instrument_statement(node->pn_kid3, f, indent + 2);        instrument_statement(node->pn_kid3, f, indent + 2);
654        fprintf(f, "%*s", indent, "");        Stream_printf(f, "%*s", indent, "");
655        fprintf(f, "}\n");        Stream_write_string(f, "}\n");
656      }      }
657      break;      break;
658    case TOK_SWITCH:    case TOK_SWITCH:
659      assert(node->pn_arity == PN_BINARY);      assert(node->pn_arity == PN_BINARY);
660      fprintf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
661      fprintf(f, "switch (");      Stream_write_string(f, "switch (");
662      instrument_expression(node->pn_left, f);      instrument_expression(node->pn_left, f);
663      fprintf(f, ") {\n");      Stream_write_string(f, ") {\n");
664      for (struct JSParseNode * p = node->pn_right->pn_head; p != NULL; p = p->pn_next) {      for (struct JSParseNode * p = node->pn_right->pn_head; p != NULL; p = p->pn_next) {
665        fprintf(f, "%*s", indent, "");        Stream_printf(f, "%*s", indent, "");
666        switch (p->pn_type) {        switch (p->pn_type) {
667        case TOK_CASE:        case TOK_CASE:
668          fprintf(f, "case ");          Stream_write_string(f, "case ");
669          instrument_expression(p->pn_left, f);          instrument_expression(p->pn_left, f);
670          fprintf(f, ":\n");          Stream_write_string(f, ":\n");
671          break;          break;
672        case TOK_DEFAULT:        case TOK_DEFAULT:
673          fprintf(f, "default:\n");          Stream_write_string(f, "default:\n");
674          break;          break;
675        default:        default:
676          abort();          abort();
# Line 593  Line 678 
678        }        }
679        instrument_statement(p->pn_right, f, indent + 2);        instrument_statement(p->pn_right, f, indent + 2);
680      }      }
681      fprintf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
682      fprintf(f, "}\n");      Stream_write_string(f, "}\n");
683      break;      break;
684    case TOK_CASE:    case TOK_CASE:
685    case TOK_DEFAULT:    case TOK_DEFAULT:
# Line 602  Line 687 
687      break;      break;
688    case TOK_WHILE:    case TOK_WHILE:
689      assert(node->pn_arity == PN_BINARY);      assert(node->pn_arity == PN_BINARY);
690      fprintf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
691      fprintf(f, "while (");      Stream_write_string(f, "while (");
692      instrument_expression(node->pn_left, f);      instrument_expression(node->pn_left, f);
693      fprintf(f, ") {\n");      Stream_write_string(f, ") {\n");
694      instrument_statement(node->pn_right, f, indent + 2);      instrument_statement(node->pn_right, f, indent + 2);
695      fprintf(f, "}\n");      Stream_write_string(f, "}\n");
696      break;      break;
697    case TOK_DO:    case TOK_DO:
698      assert(node->pn_arity == PN_BINARY);      assert(node->pn_arity == PN_BINARY);
699      fprintf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
700      fprintf(f, "do {\n");      Stream_write_string(f, "do {\n");
701      instrument_statement(node->pn_left, f, indent + 2);      instrument_statement(node->pn_left, f, indent + 2);
702      fprintf(f, "}\n");      Stream_write_string(f, "}\n");
703      fprintf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
704      fprintf(f, "while (");      Stream_write_string(f, "while (");
705      instrument_expression(node->pn_right, f);      instrument_expression(node->pn_right, f);
706      fprintf(f, ");\n");      Stream_write_string(f, ");\n");
707      break;      break;
708    case TOK_FOR:    case TOK_FOR:
709      assert(node->pn_arity == PN_BINARY);      assert(node->pn_arity == PN_BINARY);
710      fprintf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
711      fprintf(f, "for (");      Stream_write_string(f, "for (");
712      switch (node->pn_left->pn_type) {      switch (node->pn_left->pn_type) {
713      case TOK_IN:      case TOK_IN:
714        /* for/in */        /* for/in */
# Line 646  Line 731 
731          break;          break;
732  */  */
733        }        }
734        fprintf(f, " in ");        Stream_write_string(f, " in ");
735        instrument_expression(node->pn_left->pn_right, f);        instrument_expression(node->pn_left->pn_right, f);
736        break;        break;
737      case TOK_RESERVED:      case TOK_RESERVED:
# Line 660  Line 745 
745            instrument_expression(node->pn_left->pn_kid1, f);            instrument_expression(node->pn_left->pn_kid1, f);
746          }          }
747        }        }
748        fprintf(f, ";");        Stream_write_string(f, ";");
749        if (node->pn_left->pn_kid2) {        if (node->pn_left->pn_kid2) {
750          fputc(' ', f);          Stream_write_char(f, ' ');
751          instrument_expression(node->pn_left->pn_kid2, f);          instrument_expression(node->pn_left->pn_kid2, f);
752        }        }
753        fprintf(f, ";");        Stream_write_string(f, ";");
754        if (node->pn_left->pn_kid3) {        if (node->pn_left->pn_kid3) {
755          fputc(' ', f);          Stream_write_char(f, ' ');
756          instrument_expression(node->pn_left->pn_kid3, f);          instrument_expression(node->pn_left->pn_kid3, f);
757        }        }
758        break;        break;
# Line 675  Line 760 
760        abort();        abort();
761        break;        break;
762      }      }
763      fprintf(f, ") {\n");      Stream_write_string(f, ") {\n");
764      instrument_statement(node->pn_right, f, indent + 2);      instrument_statement(node->pn_right, f, indent + 2);
765      fprintf(f, "}\n");      Stream_write_string(f, "}\n");
766      break;      break;
767    case TOK_THROW:    case TOK_THROW:
768      assert(node->pn_arity == PN_UNARY);      assert(node->pn_arity == PN_UNARY);
769      fprintf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
770      fprintf(f, "throw ");      Stream_write_string(f, "throw ");
771      instrument_expression(node->pn_u.unary.kid, f);      instrument_expression(node->pn_u.unary.kid, f);
772      fprintf(f, ";\n");      Stream_write_string(f, ";\n");
773      break;      break;
774    case TOK_TRY:    case TOK_TRY:
775      fprintf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
776      fprintf(f, "try {\n");      Stream_write_string(f, "try {\n");
777      instrument_statement(node->pn_kid1, f, indent + 2);      instrument_statement(node->pn_kid1, f, indent + 2);
778      fprintf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
779      fprintf(f, "}\n");      Stream_write_string(f, "}\n");
780      {      {
781        for (JSParseNode * catch = node->pn_kid2; catch != NULL; catch = catch->pn_kid2) {        for (JSParseNode * catch = node->pn_kid2; catch != NULL; catch = catch->pn_kid2) {
782          assert(catch->pn_type == TOK_CATCH);          assert(catch->pn_type == TOK_CATCH);
783          fprintf(f, "%*s", indent, "");          Stream_printf(f, "%*s", indent, "");
784          fprintf(f, "catch (");          Stream_write_string(f, "catch (");
785          assert(catch->pn_kid1->pn_arity == PN_NAME);          assert(catch->pn_kid1->pn_arity == PN_NAME);
786          print_string_atom(catch->pn_kid1->pn_atom, f);          print_string_atom(catch->pn_kid1->pn_atom, f);
787          if (catch->pn_kid1->pn_expr) {          if (catch->pn_kid1->pn_expr) {
788            fprintf(f, " if ");            Stream_write_string(f, " if ");
789            instrument_expression(catch->pn_kid1->pn_expr, f);            instrument_expression(catch->pn_kid1->pn_expr, f);
790          }          }
791          fprintf(f, ") {\n");          Stream_write_string(f, ") {\n");
792          instrument_statement(catch->pn_kid3, f, indent + 2);          instrument_statement(catch->pn_kid3, f, indent + 2);
793          fprintf(f, "%*s", indent, "");          Stream_printf(f, "%*s", indent, "");
794          fprintf(f, "}\n");          Stream_write_string(f, "}\n");
795        }        }
796      }      }
797      if (node->pn_kid3) {      if (node->pn_kid3) {
798        fprintf(f, "%*s", indent, "");        Stream_printf(f, "%*s", indent, "");
799        fprintf(f, "finally {\n");        Stream_write_string(f, "finally {\n");
800        instrument_statement(node->pn_kid3, f, indent + 2);        instrument_statement(node->pn_kid3, f, indent + 2);
801        fprintf(f, "%*s", indent, "");        Stream_printf(f, "%*s", indent, "");
802        fprintf(f, "}\n");        Stream_write_string(f, "}\n");
803      }      }
804      break;      break;
805    case TOK_CATCH:    case TOK_CATCH:
# Line 723  Line 808 
808    case TOK_BREAK:    case TOK_BREAK:
809    case TOK_CONTINUE:    case TOK_CONTINUE:
810      assert(node->pn_arity == PN_NAME || node->pn_arity == PN_NULLARY);      assert(node->pn_arity == PN_NAME || node->pn_arity == PN_NULLARY);
811      fprintf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
812      fputs(node->pn_type == TOK_BREAK? "break": "continue", f);      Stream_write_string(f, node->pn_type == TOK_BREAK? "break": "continue");
813      JSAtom * atom = node->pn_u.name.atom;      JSAtom * atom = node->pn_u.name.atom;
814      if (atom != NULL) {      if (atom != NULL) {
815        fputc(' ', f);        Stream_write_char(f, ' ');
816        print_string_atom(node->pn_atom, f);        print_string_atom(node->pn_atom, f);
817      }      }
818      fprintf(f, ";\n");      Stream_write_string(f, ";\n");
819      break;      break;
820    case TOK_WITH:    case TOK_WITH:
821      assert(node->pn_arity == PN_BINARY);      assert(node->pn_arity == PN_BINARY);
822      fprintf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
823      fprintf(f, "with (");      Stream_write_string(f, "with (");
824      instrument_expression(node->pn_left, f);      instrument_expression(node->pn_left, f);
825      fprintf(f, ") {\n");      Stream_write_string(f, ") {\n");
826      instrument_statement(node->pn_right, f, indent + 2);      instrument_statement(node->pn_right, f, indent + 2);
827      fprintf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
828      fprintf(f, "}\n");      Stream_write_string(f, "}\n");
829      break;      break;
830    case TOK_VAR:    case TOK_VAR:
831      instrument_var_statement(node, f, indent);      instrument_var_statement(node, f, indent);
832      fprintf(f, ";\n");      Stream_write_string(f, ";\n");
833      break;      break;
834    case TOK_RETURN:    case TOK_RETURN:
835      assert(node->pn_arity == PN_UNARY);      assert(node->pn_arity == PN_UNARY);
836      fprintf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
837      fprintf(f, "return");      Stream_write_string(f, "return");
838      if (node->pn_kid != NULL) {      if (node->pn_kid != NULL) {
839        fprintf(f, " ");        Stream_write_char(f, ' ');
840        instrument_expression(node->pn_kid, f);        instrument_expression(node->pn_kid, f);
841      }      }
842      fprintf(f, ";\n");      Stream_write_string(f, ";\n");
843      break;      break;
844    case TOK_SEMI:    case TOK_SEMI:
845      assert(node->pn_arity == PN_UNARY);      assert(node->pn_arity == PN_UNARY);
846      fprintf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
847      if (node->pn_kid != NULL) {      if (node->pn_kid != NULL) {
848        instrument_expression(node->pn_kid, f);        instrument_expression(node->pn_kid, f);
849      }      }
850      fprintf(f, ";\n");      Stream_write_string(f, ";\n");
851      break;      break;
852    case TOK_COLON:    case TOK_COLON:
853      assert(node->pn_arity == PN_NAME);      assert(node->pn_arity == PN_NAME);
# Line 770  Line 855 
855      This one is tricky: can't output instrumentation between the label and the      This one is tricky: can't output instrumentation between the label and the
856      statement it's supposed to label ...      statement it's supposed to label ...
857      */      */
858      fprintf(f, "%*s", indent < 2? 0: indent - 2, "");      Stream_printf(f, "%*s", indent < 2? 0: indent - 2, "");
859      print_string_atom(node->pn_atom, f);      print_string_atom(node->pn_atom, f);
860      fprintf(f, ":\n");      Stream_write_string(f, ":\n");
861      /*      /*
862      ... use output_statement instead of instrument_statement.      ... use output_statement instead of instrument_statement.
863      */      */
# Line 788  Line 873 
873  TOK_FUNCTION is handled as a statement and as an expression.  TOK_FUNCTION is handled as a statement and as an expression.
874  TOK_EXPORT, TOK_IMPORT are not handled.  TOK_EXPORT, TOK_IMPORT are not handled.
875  */  */
876  static void instrument_statement(JSParseNode * node, FILE * f, int indent) {  static void instrument_statement(JSParseNode * node, Stream * f, int indent) {
877    if (node->pn_type != TOK_LC) {    if (node->pn_type != TOK_LC) {
878      int line = node->pn_pos.begin.lineno;      uint16_t line = node->pn_pos.begin.lineno;
879        if (line > num_lines) {
880          fatal("%s: script contains more than 65,535 lines", file_id);
881        }
882    
883      /* the root node has line number 0 */      /* the root node has line number 0 */
884      if (line != 0) {      if (line != 0) {
885        fprintf(f, "%*s", indent, "");        Stream_printf(f, "%*s", indent, "");
886        fprintf(f, "_$jscoverage['%s'][%d]++;\n", file_id, line);        Stream_printf(f, "_$jscoverage['%s'][%d]++;\n", file_id, line);
887        lines[line - 1] = 1;        lines[line - 1] = 1;
888      }      }
889    }    }
890    output_statement(node, f, indent);    output_statement(node, f, indent);
891  }  }
892    
893  static void instrument_js_stream(const char * id, int line, FILE * input, FILE * output, const char * temporary_file_name) {  static bool characters_start_with(const jschar * characters, size_t line_start, size_t line_end, const char * prefix) {
894      const jschar * characters_end = characters + line_end;
895      const jschar * cp = characters + line_start;
896      const char * bp = prefix;
897      for (;;) {
898        if (*bp == '\0') {
899          return true;
900        }
901        else if (cp == characters_end) {
902          return false;
903        }
904        else if (*cp != *bp) {
905          return false;
906        }
907        bp++;
908        cp++;
909      }
910    }
911    
912    void jscoverage_instrument_js(const char * id, const uint16_t * characters, size_t num_characters, Stream * output) {
913    file_id = id;    file_id = id;
914    
915    /* scan the javascript */    /* scan the javascript */
916    JSTokenStream * token_stream = js_NewFileTokenStream(context, NULL, input);    JSTokenStream * token_stream = js_NewTokenStream(context, characters, num_characters, NULL, 1, NULL);
917    if (token_stream == NULL) {    if (token_stream == NULL) {
918      fatal("cannot create token stream from file: %s", file_id);      fatal("cannot create token stream from file: %s", file_id);
919    }    }
# Line 815  Line 923 
923    if (node == NULL) {    if (node == NULL) {
924      fatal("parse error in file: %s", file_id);      fatal("parse error in file: %s", file_id);
925    }    }
926    int num_lines = node->pn_pos.end.lineno;    num_lines = node->pn_pos.end.lineno;
927    lines = xmalloc(num_lines);    lines = xmalloc(num_lines);
928    for (int i = 0; i < num_lines; i++) {    for (int i = 0; i < num_lines; i++) {
929      lines[i] = 0;      lines[i] = 0;
930    }    }
931    
932    /*    /*
933    Create a temporary file - we can't write directly to the output because we    An instrumented JavaScript file has 3 sections:
934    need to know the line number info first.    1. initialization
935      2. instrumented source code
936      3. original source code
937    */    */
   FILE * temporary = fopen(temporary_file_name, "w+");  
   if (temporary == NULL) {  
     fatal("cannot create temporary file for script: %s", file_id);  
   }  
938    
939    /* write instrumented javascript to the temporary */    Stream * instrumented = Stream_new(0);
940    instrument_statement(node, temporary, 0);    instrument_statement(node, instrumented, 0);
941    
942    /* write line number info to the output */    /* write line number info to the output */
943    fprintf(output, "/* automatically generated by JSCoverage - do not edit */\n");    Stream_write_string(output, "/* automatically generated by JSCoverage - do not edit */\n");
944    fprintf(output, "if (! top._$jscoverage) {\n  top._$jscoverage = {};\n}\n");    Stream_write_string(output, "if (! top._$jscoverage) {\n  top._$jscoverage = {};\n}\n");
945    fprintf(output, "var _$jscoverage = top._$jscoverage;\n");    Stream_write_string(output, "var _$jscoverage = top._$jscoverage;\n");
946    fprintf(output, "if (! _$jscoverage['%s']) {\n", file_id);    Stream_printf(output, "if (! _$jscoverage['%s']) {\n", file_id);
947    fprintf(output, "  _$jscoverage['%s'] = [];\n", file_id);    Stream_printf(output, "  _$jscoverage['%s'] = [];\n", file_id);
948    for (int i = 0; i < num_lines; i++) {    for (int i = 0; i < num_lines; i++) {
949      if (lines[i]) {      if (lines[i]) {
950        fprintf(output, "  _$jscoverage['%s'][%d] = 0;\n", file_id, i + 1);        Stream_printf(output, "  _$jscoverage['%s'][%d] = 0;\n", file_id, i + 1);
951      }      }
952    }    }
953    fprintf(output, "}\n");    Stream_write_string(output, "}\n");
954      free(lines);
955    lines = NULL;    lines = NULL;
956    
957    /* copy the temporary to the output */    /* copy the instrumented source code to the output */
958    fseek(temporary, 0, SEEK_SET);    Stream_write(output, instrumented->data, instrumented->length);
959    copy_stream(temporary, output);  
960      /* conditionals */
961      bool has_conditionals = false;
962      size_t line_number = 0;
963      size_t i = 0;
964      while (i < num_characters) {
965        line_number++;
966        size_t line_start = i;
967        jschar c;
968        bool done = false;
969        while (! done && i < num_characters) {
970          c = characters[i];
971          switch (c) {
972          case '\r':
973          case '\n':
974          case 0x2028:
975          case 0x2029:
976            done = true;
977            break;
978          default:
979            i++;
980            break;
981          }
982        }
983        size_t line_end = i;
984        if (i < num_characters) {
985          i++;
986          if (c == '\r' && i < num_characters && characters[i] == '\n') {
987            i++;
988          }
989        }
990        if (characters_start_with(characters, line_start, line_end, "//#JSCOVERAGE_IF")) {
991          if (! has_conditionals) {
992            has_conditionals = true;
993            Stream_printf(output, "_$jscoverage['%s'].conditionals = [];\n", file_id);
994          }
995          Stream_write_string(output, "if (!(");
996          for (size_t j = line_start + 16; j < line_end; j++) {
997            jschar c = characters[j];
998            if (c == '\t' || (32 <= c && c <= 126)) {
999              Stream_write_char(output, c);
1000            }
1001            else {
1002              Stream_printf(output, "\\u%04x", c);
1003            }
1004          }
1005          Stream_write_string(output, ")) {\n");
1006          Stream_printf(output, "  _$jscoverage['%s'].conditionals[%d] = ", file_id, line_number);
1007        }
1008        else if (characters_start_with(characters, line_start, line_end, "//#JSCOVERAGE_ENDIF")) {
1009          Stream_printf(output, "%d;\n", line_number);
1010          Stream_printf(output, "}\n");
1011        }
1012      }
1013    
1014      /* copy the original source to the output */
1015      Stream_printf(output, "_$jscoverage['%s'].source = ", file_id);
1016      jscoverage_write_source(id, characters, num_characters, output);
1017      Stream_printf(output, ";\n");
1018    
1019    fclose(temporary);    Stream_delete(instrumented);
1020    
1021    file_id = NULL;    file_id = NULL;
1022  }  }
1023    
1024  void jscoverage_instrument_js(const char * id, FILE * input, FILE * output, const char * temporary_file_name) {  void jscoverage_write_source(const char * id, const jschar * characters, size_t num_characters, Stream * output) {
1025    instrument_js_stream(id, 0, input, output, temporary_file_name);    Stream_write_string(output, "[");
1026      if (jscoverage_highlight) {
1027        Stream * highlighted_stream = Stream_new(num_characters);
1028        jscoverage_highlight_js(context, id, characters, num_characters, highlighted_stream);
1029        size_t i = 0;
1030        while (i < highlighted_stream->length) {
1031          if (i > 0) {
1032            Stream_write_char(output, ',');
1033          }
1034    
1035          Stream_write_char(output, '"');
1036          bool done = false;
1037          while (! done) {
1038            char c = highlighted_stream->data[i];
1039            switch (c) {
1040            case 0x8:
1041              /* backspace */
1042              Stream_write_string(output, "\\b");
1043              break;
1044            case 0x9:
1045              /* horizontal tab */
1046              Stream_write_string(output, "\\t");
1047              break;
1048            case 0xa:
1049              /* line feed (new line) */
1050              done = true;
1051              break;
1052            case 0xb:
1053              /* vertical tab */
1054              Stream_write_string(output, "\\v");
1055              break;
1056            case 0xc:
1057              /* form feed */
1058              Stream_write_string(output, "\\f");
1059              break;
1060            case 0xd:
1061              /* carriage return */
1062              done = true;
1063              if (i + 1 < highlighted_stream->length && highlighted_stream->data[i + 1] == '\n') {
1064                i++;
1065              }
1066              break;
1067            case '"':
1068              Stream_write_string(output, "\\\"");
1069              break;
1070            case '\\':
1071              Stream_write_string(output, "\\\\");
1072              break;
1073            default:
1074              Stream_write_char(output, c);
1075              break;
1076            }
1077            i++;
1078            if (i >= highlighted_stream->length) {
1079              done = true;
1080            }
1081          }
1082          Stream_write_char(output, '"');
1083        }
1084        Stream_delete(highlighted_stream);
1085      }
1086      else {
1087        size_t i = 0;
1088        while (i < num_characters) {
1089          if (i > 0) {
1090            Stream_write_char(output, ',');
1091          }
1092    
1093          Stream_write_char(output, '"');
1094          bool done = false;
1095          while (! done) {
1096            jschar c = characters[i];
1097            switch (c) {
1098            case 0x8:
1099              /* backspace */
1100              Stream_write_string(output, "\\b");
1101              break;
1102            case 0x9:
1103              /* horizontal tab */
1104              Stream_write_string(output, "\\t");
1105              break;
1106            case 0xa:
1107              /* line feed (new line) */
1108              done = true;
1109              break;
1110            case 0xb:
1111              /* vertical tab */
1112              Stream_write_string(output, "\\v");
1113              break;
1114            case 0xc:
1115              /* form feed */
1116              Stream_write_string(output, "\\f");
1117              break;
1118            case 0xd:
1119              /* carriage return */
1120              done = true;
1121              if (i + 1 < num_characters && characters[i + 1] == '\n') {
1122                i++;
1123              }
1124              break;
1125            case '"':
1126              Stream_write_string(output, "\\\"");
1127              break;
1128            case '\\':
1129              Stream_write_string(output, "\\\\");
1130              break;
1131            case '&':
1132              Stream_write_string(output, "&amp;");
1133              break;
1134            case '<':
1135              Stream_write_string(output, "&lt;");
1136              break;
1137            case '>':
1138              Stream_write_string(output, "&gt;");
1139              break;
1140            case 0x2028:
1141            case 0x2029:
1142              done = true;
1143              break;
1144            default:
1145              if (32 <= c && c <= 126) {
1146                Stream_write_char(output, c);
1147              }
1148              else {
1149                Stream_printf(output, "&#%d;", c);
1150              }
1151              break;
1152            }
1153            i++;
1154            if (i >= num_characters) {
1155              done = true;
1156            }
1157          }
1158          Stream_write_char(output, '"');
1159        }
1160      }
1161      Stream_write_string(output, "]");
1162    }
1163    
1164    void jscoverage_copy_resources(const char * destination_directory) {
1165      copy_resource("jscoverage.html", destination_directory);
1166      copy_resource("jscoverage.css", destination_directory);
1167      copy_resource("jscoverage.js", destination_directory);
1168      copy_resource("jscoverage-ie.css", destination_directory);
1169      copy_resource("jscoverage-throbber.gif", destination_directory);
1170      copy_resource("jscoverage-highlight.css", destination_directory);
1171    }
1172    
1173    /*
1174    coverage reports
1175    */
1176    
1177    struct FileCoverageList {
1178      FileCoverage * file_coverage;
1179      struct FileCoverageList * next;
1180    };
1181    
1182    struct Coverage {
1183      JSHashTable * coverage_table;
1184      struct FileCoverageList * coverage_list;
1185    };
1186    
1187    static int compare_strings(const void * p1, const void * p2) {
1188      return strcmp(p1, p2) == 0;
1189    }
1190    
1191    Coverage * Coverage_new(void) {
1192      Coverage * result = xmalloc(sizeof(Coverage));
1193      result->coverage_table = JS_NewHashTable(1024, JS_HashString, compare_strings, NULL, NULL, NULL);
1194      if (result->coverage_table == NULL) {
1195        fatal("cannot create hash table");
1196      }
1197      result->coverage_list = NULL;
1198      return result;
1199    }
1200    
1201    void Coverage_delete(Coverage * coverage) {
1202      JS_HashTableDestroy(coverage->coverage_table);
1203      struct FileCoverageList * p = coverage->coverage_list;
1204      while (p != NULL) {
1205        free(p->file_coverage->coverage_lines);
1206        if (p->file_coverage->source_lines != NULL) {
1207          for (uint32 i = 0; i < p->file_coverage->num_source_lines; i++) {
1208            free(p->file_coverage->source_lines[i]);
1209          }
1210          free(p->file_coverage->source_lines);
1211        }
1212        free(p->file_coverage->id);
1213        free(p->file_coverage);
1214        struct FileCoverageList * q = p;
1215        p = p->next;
1216        free(q);
1217      }
1218      free(coverage);
1219    }
1220    
1221    struct EnumeratorArg {
1222      CoverageForeachFunction f;
1223      void * p;
1224    };
1225    
1226    static intN enumerator(JSHashEntry * entry, intN i, void * arg) {
1227      struct EnumeratorArg * enumerator_arg = arg;
1228      enumerator_arg->f(entry->value, i, enumerator_arg->p);
1229      return 0;
1230    }
1231    
1232    void Coverage_foreach_file(Coverage * coverage, CoverageForeachFunction f, void * p) {
1233      struct EnumeratorArg enumerator_arg;
1234      enumerator_arg.f = f;
1235      enumerator_arg.p = p;
1236      JS_HashTableEnumerateEntries(coverage->coverage_table, enumerator, &enumerator_arg);
1237    }
1238    
1239    int jscoverage_parse_json(Coverage * coverage, const uint8_t * json, size_t length) {
1240      jschar * base = js_InflateString(context, (char *) json, &length);
1241      if (base == NULL) {
1242        fatal("out of memory");
1243      }
1244    
1245      jschar * parenthesized_json = xnew(jschar, addst(length, 2));
1246      parenthesized_json[0] = '(';
1247      memcpy(parenthesized_json + 1, base, mulst(length, sizeof(jschar)));
1248      parenthesized_json[length + 1] = ')';
1249    
1250      JS_free(context, base);
1251    
1252      JSTokenStream * token_stream = js_NewTokenStream(context, parenthesized_json, length + 2, NULL, 1, NULL);
1253      if (token_stream == NULL) {
1254        fatal("cannot create token stream");
1255      }
1256    
1257      JSParseNode * root = js_ParseTokenStream(context, global, token_stream);
1258      free(parenthesized_json);
1259      if (root == NULL) {
1260        return -1;
1261      }
1262    
1263      /* root node must be TOK_LC */
1264      if (root->pn_type != TOK_LC) {
1265        return -1;
1266      }
1267      JSParseNode * semi = root->pn_u.list.head;
1268    
1269      /* the list must be TOK_SEMI and it must contain only one element */
1270      if (semi->pn_type != TOK_SEMI || semi->pn_next != NULL) {
1271        return -1;
1272      }
1273      JSParseNode * parenthesized = semi->pn_kid;
1274    
1275      /* this must be a parenthesized expression */
1276      if (parenthesized->pn_type != TOK_RP) {
1277        return -1;
1278      }
1279      JSParseNode * object = parenthesized->pn_kid;
1280    
1281      /* this must be an object literal */
1282      if (object->pn_type != TOK_RC) {
1283        return -1;
1284      }
1285    
1286      for (JSParseNode * p = object->pn_head; p != NULL; p = p->pn_next) {
1287        /* every element of this list must be TOK_COLON */
1288        if (p->pn_type != TOK_COLON) {
1289          return -1;
1290        }
1291    
1292        /* the key must be a string representing the file */
1293        JSParseNode * key = p->pn_left;
1294        if (key->pn_type != TOK_STRING || ! ATOM_IS_STRING(key->pn_atom)) {
1295          return -1;
1296        }
1297        char * id_bytes = JS_GetStringBytes(ATOM_TO_STRING(key->pn_atom));
1298    
1299        /* the value must be an object literal OR an array */
1300        JSParseNode * value = p->pn_right;
1301        if (! (value->pn_type == TOK_RC || value->pn_type == TOK_RB)) {
1302          return -1;
1303        }
1304    
1305        JSParseNode * array = NULL;
1306        JSParseNode * source = NULL;
1307        if (value->pn_type == TOK_RB) {
1308          /* an array */
1309          array = value;
1310        }
1311        else if (value->pn_type == TOK_RC) {
1312          /* an object literal */
1313          if (value->pn_count != 2) {
1314            return -1;
1315          }
1316          for (JSParseNode * element = value->pn_head; element != NULL; element = element->pn_next) {
1317            if (element->pn_type != TOK_COLON) {
1318              return -1;
1319            }
1320            JSParseNode * left = element->pn_left;
1321            if (left->pn_type != TOK_STRING || ! ATOM_IS_STRING(left->pn_atom)) {
1322              return -1;
1323            }
1324            const char * s = JS_GetStringBytes(ATOM_TO_STRING(left->pn_atom));
1325            if (strcmp(s, "coverage") == 0) {
1326              array = element->pn_right;
1327              if (array->pn_type != TOK_RB) {
1328                return -1;
1329              }
1330            }
1331            else if (strcmp(s, "source") == 0) {
1332              source = element->pn_right;
1333              if (source->pn_type != TOK_RB) {
1334                return -1;
1335              }
1336            }
1337            else {
1338              return -1;
1339            }
1340          }
1341        }
1342        else {
1343          return -1;
1344        }
1345    
1346        if (array == NULL) {
1347          return -1;
1348        }
1349    
1350        /* look up the file in the coverage table */
1351        FileCoverage * file_coverage = JS_HashTableLookup(coverage->coverage_table, id_bytes);
1352        if (file_coverage == NULL) {
1353          /* not there: create a new one */
1354          char * id = xstrdup(id_bytes);
1355          file_coverage = xmalloc(sizeof(FileCoverage));
1356          file_coverage->id = id;
1357          file_coverage->num_coverage_lines = array->pn_count;
1358          file_coverage->coverage_lines = xnew(int, array->pn_count);
1359          if (source == NULL) {
1360            file_coverage->source_lines = NULL;
1361          }
1362          else {
1363            file_coverage->num_source_lines = source->pn_count;
1364            file_coverage->source_lines = xnew(char *, source->pn_count);
1365            uint32 i = 0;
1366            for (JSParseNode * element = source->pn_head; element != NULL; element = element->pn_next, i++) {
1367              if (element->pn_type != TOK_STRING) {
1368                return -1;
1369              }
1370              file_coverage->source_lines[i] = xstrdup(JS_GetStringBytes(ATOM_TO_STRING(element->pn_atom)));
1371            }
1372            assert(i == source->pn_count);
1373          }
1374    
1375          /* set coverage for all lines */
1376          uint32 i = 0;
1377          for (JSParseNode * element = array->pn_head; element != NULL; element = element->pn_next, i++) {
1378            if (element->pn_type == TOK_NUMBER) {
1379              file_coverage->coverage_lines[i] = (int) element->pn_dval;
1380            }
1381            else if (element->pn_type == TOK_PRIMARY && element->pn_op == JSOP_NULL) {
1382              file_coverage->coverage_lines[i] = -1;
1383            }
1384            else {
1385              return -1;
1386            }
1387          }
1388          assert(i == array->pn_count);
1389    
1390          /* add to the hash table */
1391          JS_HashTableAdd(coverage->coverage_table, id, file_coverage);
1392          struct FileCoverageList * coverage_list = xmalloc(sizeof(struct FileCoverageList));
1393          coverage_list->file_coverage = file_coverage;
1394          coverage_list->next = coverage->coverage_list;
1395          coverage->coverage_list = coverage_list;
1396        }
1397        else {
1398          /* sanity check */
1399          assert(strcmp(file_coverage->id, id_bytes) == 0);
1400          if (file_coverage->num_coverage_lines != array->pn_count) {
1401            return -2;
1402          }
1403    
1404          /* merge the coverage */
1405          uint32 i = 0;
1406          for (JSParseNode * element = array->pn_head; element != NULL; element = element->pn_next, i++) {
1407            if (element->pn_type == TOK_NUMBER) {
1408              if (file_coverage->coverage_lines[i] == -1) {
1409                return -2;
1410              }
1411              file_coverage->coverage_lines[i] += (int) element->pn_dval;
1412            }
1413            else if (element->pn_type == TOK_PRIMARY && element->pn_op == JSOP_NULL) {
1414              if (file_coverage->coverage_lines[i] != -1) {
1415                return -2;
1416              }
1417            }
1418            else {
1419              return -1;
1420            }
1421          }
1422          assert(i == array->pn_count);
1423    
1424          /* if this JSON file has source, use it */
1425          if (file_coverage->source_lines == NULL && source != NULL) {
1426            file_coverage->num_source_lines = source->pn_count;
1427            file_coverage->source_lines = xnew(char *, source->pn_count);
1428            uint32 i = 0;
1429            for (JSParseNode * element = source->pn_head; element != NULL; element = element->pn_next, i++) {
1430              if (element->pn_type != TOK_STRING) {
1431                return -1;
1432              }
1433              file_coverage->source_lines[i] = xstrdup(JS_GetStringBytes(ATOM_TO_STRING(element->pn_atom)));
1434            }
1435            assert(i == source->pn_count);
1436          }
1437        }
1438      }
1439    
1440      return 0;
1441  }  }

Legend:
Removed from v.87  
changed lines
  Added in v.214

  ViewVC Help
Powered by ViewVC 1.1.24