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

Diff of /trunk/instrument-js.cpp

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

revision 70 by siliconforks, Thu Nov 22 02:52:38 2007 UTC revision 174 by siliconforks, Sat Sep 20 23:27:14 2008 UTC
# Line 1  Line 1 
1  /*  /*
2      instrument-js.c - JavaScript instrumentation routines      instrument-js.c - JavaScript instrumentation routines
3      Copyright (C) 2007 siliconforks.com      Copyright (C) 2007, 2008 siliconforks.com
4    
5      This program is free software; you can redistribute it and/or modify      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      it under the terms of the GNU General Public License as published by
# 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 "resource-manager.h"
39  #include "util.h"  #include "util.h"
40    
41  static JSRuntime * runtime = NULL;  static JSRuntime * runtime = NULL;
# Line 71  Line 75 
75    JS_DestroyRuntime(runtime);    JS_DestroyRuntime(runtime);
76  }  }
77    
78  static void print_string(JSString * s, FILE * f) {  static void print_string(JSString * s, Stream * f) {
79    for (int i = 0; i < s->length; i++) {    size_t length = JSSTRING_LENGTH(s);
80      char c = s->chars[i];    jschar * characters = JSSTRING_CHARS(s);
81      fputc(c, f);    for (size_t i = 0; i < length; i++) {
82        jschar c = characters[i];
83        if (32 <= c && c <= 126) {
84          switch (c) {
85          case '"':
86            Stream_write_string(f, "\\\"");
87            break;
88    /*
89          case '\'':
90            Stream_write_string(f, "\\'");
91            break;
92    */
93          case '\\':
94            Stream_write_string(f, "\\\\");
95            break;
96          default:
97            Stream_write_char(f, c);
98            break;
99          }
100        }
101        else {
102          switch (c) {
103          case 0x8:
104            Stream_write_string(f, "\\b");
105            break;
106          case 0x9:
107            Stream_write_string(f, "\\t");
108            break;
109          case 0xa:
110            Stream_write_string(f, "\\n");
111            break;
112          case 0xb:
113            Stream_write_string(f, "\\v");
114            break;
115          case 0xc:
116            Stream_write_string(f, "\\f");
117            break;
118          case 0xd:
119            Stream_write_string(f, "\\r");
120            break;
121          default:
122            Stream_printf(f, "\\u%04x", c);
123            break;
124          }
125        }
126    }    }
127  }  }
128    
129  static void print_string_atom(JSAtom * atom, FILE * f) {  static void print_string_atom(JSAtom * atom, Stream * f) {
130    assert(ATOM_IS_STRING(atom));    assert(ATOM_IS_STRING(atom));
131    JSString * s = ATOM_TO_STRING(atom);    JSString * s = ATOM_TO_STRING(atom);
132    print_string(s, f);    print_string(s, f);
133  }  }
134    
135  static void print_string_jsval(jsval value, FILE * f) {  static void print_regex(jsval value, Stream * f) {
136    assert(JSVAL_IS_STRING(value));    assert(JSVAL_IS_STRING(value));
137    JSString * s = JSVAL_TO_STRING(value);    JSString * s = JSVAL_TO_STRING(value);
138    print_string(s, f);    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          Stream_write_char(f, c);
144        }
145        else {
146          Stream_printf(f, "\\u%04x", c);
147        }
148      }
149  }  }
150    
151  static void print_quoted_string_atom(JSAtom * atom, FILE * f) {  static void print_quoted_string_atom(JSAtom * atom, Stream * f) {
152    assert(ATOM_IS_STRING(atom));    assert(ATOM_IS_STRING(atom));
153    JSString * s = ATOM_TO_STRING(atom);    JSString * s = ATOM_TO_STRING(atom);
154    JSString * quoted = js_QuoteString(context, s, '"');    Stream_write_char(f, '"');
155    print_string(quoted, f);    print_string(s, f);
156      Stream_write_char(f, '"');
157  }  }
158    
159  static const char * get_op(uint8 op) {  static const char * get_op(uint8 op) {
# Line 142  Line 201 
201    }    }
202  }  }
203    
204  static void instrument_expression(JSParseNode * node, FILE * f);  static void instrument_expression(JSParseNode * node, Stream * f);
205  static void instrument_statement(JSParseNode * node, FILE * f, int indent);  static void instrument_statement(JSParseNode * node, Stream * f, int indent);
206    
207    enum FunctionType {
208      FUNCTION_NORMAL,
209      FUNCTION_GETTER_OR_SETTER
210    };
211    
212    static void instrument_function(JSParseNode * node, Stream * f, int indent, enum FunctionType type) {
213      assert(node->pn_arity == PN_FUNC);
214      assert(ATOM_IS_OBJECT(node->pn_funAtom));
215      JSObject * object = ATOM_TO_OBJECT(node->pn_funAtom);
216      assert(JS_ObjectIsFunction(context, object));
217      JSFunction * function = (JSFunction *) JS_GetPrivate(context, object);
218      assert(function);
219      assert(object == function->object);
220      Stream_printf(f, "%*s", indent, "");
221      if (type == FUNCTION_NORMAL) {
222        Stream_write_string(f, "function");
223      }
224    
225  static void instrument_function(JSParseNode * node, FILE * f, int indent) {    /* function name */
226      assert(node->pn_arity == PN_FUNC);    if (function->atom) {
227      assert(ATOM_IS_OBJECT(node->pn_funAtom));      Stream_write_char(f, ' ');
228      JSObject * object = ATOM_TO_OBJECT(node->pn_funAtom);      print_string_atom(function->atom, f);
229      assert(JS_ObjectIsFunction(context, object));    }
     JSFunction * function = (JSFunction *) JS_GetPrivate(context, object);  
     assert(function);  
     assert(object == function->object);  
     fprintf(f, "%*s", indent, "");  
     fprintf(f, "function");  
   
     /* function name */  
     if (function->atom) {  
       fputc(' ', f);  
       print_string_atom(function->atom, f);  
     }  
   
     /* function parameters */  
     fprintf(f, "(");  
     JSAtom ** params = xmalloc(function->nargs * sizeof(JSAtom *));  
     for (int i = 0; i < function->nargs; i++) {  
       /* initialize to NULL for sanity check */  
       params[i] = NULL;  
     }  
     JSScope * scope = OBJ_SCOPE(object);  
     for (JSScopeProperty * scope_property = SCOPE_LAST_PROP(scope); scope_property != NULL; scope_property = scope_property->parent) {  
       if (scope_property->getter != js_GetArgument) {  
         continue;  
       }  
       assert(scope_property->flags & SPROP_HAS_SHORTID);  
       assert((uint16) scope_property->shortid < function->nargs);  
       assert(JSID_IS_ATOM(scope_property->id));  
       params[(uint16) scope_property->shortid] = JSID_TO_ATOM(scope_property->id);  
     }  
     for (int i = 0; i < function->nargs; i++) {  
       assert(params[i] != NULL);  
       if (i > 0) {  
         fprintf(f, ", ");  
       }  
       if (ATOM_IS_STRING(params[i])) {  
         print_string_atom(params[i], f);  
       }  
     }  
     fprintf(f, ") {\n");  
     free(params);  
230    
231      /* function body */    /* function parameters */
232      instrument_statement(node->pn_body, f, indent + 2);    Stream_write_string(f, "(");
233      JSAtom ** params = xnew(JSAtom *, function->nargs);
234      for (int i = 0; i < function->nargs; i++) {
235        /* initialize to NULL for sanity check */
236        params[i] = NULL;
237      }
238      JSScope * scope = OBJ_SCOPE(object);
239      for (JSScopeProperty * scope_property = SCOPE_LAST_PROP(scope); scope_property != NULL; scope_property = scope_property->parent) {
240        if (scope_property->getter != js_GetArgument) {
241          continue;
242        }
243        assert(scope_property->flags & SPROP_HAS_SHORTID);
244        assert((uint16) scope_property->shortid < function->nargs);
245        assert(JSID_IS_ATOM(scope_property->id));
246        params[(uint16) scope_property->shortid] = JSID_TO_ATOM(scope_property->id);
247      }
248      for (int i = 0; i < function->nargs; i++) {
249        assert(params[i] != NULL);
250        if (i > 0) {
251          Stream_write_string(f, ", ");
252        }
253        if (ATOM_IS_STRING(params[i])) {
254          print_string_atom(params[i], f);
255        }
256      }
257      Stream_write_string(f, ") {\n");
258      free(params);
259    
260      fprintf(f, "}\n");    /* function body */
261      instrument_statement(node->pn_body, f, indent + 2);
262    
263      Stream_write_string(f, "}\n");
264  }  }
265    
266  static void instrument_function_call(JSParseNode * node, FILE * f) {  static void instrument_function_call(JSParseNode * node, Stream * f) {
267    instrument_expression(node->pn_head, f);    instrument_expression(node->pn_head, f);
268    fputc('(', f);    Stream_write_char(f, '(');
269    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) {
270      if (p != node->pn_head->pn_next) {      if (p != node->pn_head->pn_next) {
271        fprintf(f, ", ");        Stream_write_string(f, ", ");
272      }      }
273      instrument_expression(p, f);      instrument_expression(p, f);
274    }    }
275    fputc(')', f);    Stream_write_char(f, ')');
276  }  }
277    
278  /*  /*
# Line 222  Line 288 
288  TOK_INSTANCEOF  binary  TOK_INSTANCEOF  binary
289  TOK_IN          binary  TOK_IN          binary
290  */  */
291  static void instrument_expression(JSParseNode * node, FILE * f) {  static void instrument_expression(JSParseNode * node, Stream * f) {
292    switch (node->pn_type) {    switch (node->pn_type) {
293    case TOK_FUNCTION:    case TOK_FUNCTION:
294      instrument_function(node, f, 0);      instrument_function(node, f, 0, FUNCTION_NORMAL);
295      break;      break;
296    case TOK_COMMA:    case TOK_COMMA:
297      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) {
298        if (p != node->pn_head) {        if (p != node->pn_head) {
299          fprintf(f, ", ");          Stream_write_string(f, ", ");
300        }        }
301        instrument_expression(p, f);        instrument_expression(p, f);
302      }      }
303      break;      break;
304    case TOK_ASSIGN:    case TOK_ASSIGN:
305      instrument_expression(node->pn_left, f);      instrument_expression(node->pn_left, f);
306      fputc(' ', f);      Stream_write_char(f, ' ');
307      switch (node->pn_op) {      switch (node->pn_op) {
308      case JSOP_ADD:      case JSOP_ADD:
309      case JSOP_SUB:      case JSOP_SUB:
# Line 250  Line 316 
316      case JSOP_BITOR:      case JSOP_BITOR:
317      case JSOP_BITXOR:      case JSOP_BITXOR:
318      case JSOP_DIV:      case JSOP_DIV:
319        fprintf(f, "%s", get_op(node->pn_op));        Stream_printf(f, "%s", get_op(node->pn_op));
320        break;        break;
321      default:      default:
322        /* do nothing - it must be a simple assignment */        /* do nothing - it must be a simple assignment */
323        break;        break;
324      }      }
325      fprintf(f, "= ");      Stream_write_string(f, "= ");
326      instrument_expression(node->pn_right, f);      instrument_expression(node->pn_right, f);
327      break;      break;
328    case TOK_HOOK:    case TOK_HOOK:
329      instrument_expression(node->pn_kid1, f);      instrument_expression(node->pn_kid1, f);
330      fprintf(f, "? ");      Stream_write_string(f, "? ");
331      instrument_expression(node->pn_kid2, f);      instrument_expression(node->pn_kid2, f);
332      fprintf(f, ": ");      Stream_write_string(f, ": ");
333      instrument_expression(node->pn_kid3, f);      instrument_expression(node->pn_kid3, f);
334      break;      break;
335    case TOK_OR:    case TOK_OR:
336      instrument_expression(node->pn_left, f);      instrument_expression(node->pn_left, f);
337      fprintf(f, " || ");      Stream_write_string(f, " || ");
338      instrument_expression(node->pn_right, f);      instrument_expression(node->pn_right, f);
339      break;      break;
340    case TOK_AND:    case TOK_AND:
341      instrument_expression(node->pn_left, f);      instrument_expression(node->pn_left, f);
342      fprintf(f, " && ");      Stream_write_string(f, " && ");
343      instrument_expression(node->pn_right, f);      instrument_expression(node->pn_right, f);
344      break;      break;
345    case TOK_BITOR:    case TOK_BITOR:
# Line 289  Line 355 
355      switch (node->pn_arity) {      switch (node->pn_arity) {
356      case PN_BINARY:      case PN_BINARY:
357        instrument_expression(node->pn_left, f);        instrument_expression(node->pn_left, f);
358        fprintf(f, " %s ", get_op(node->pn_op));        Stream_printf(f, " %s ", get_op(node->pn_op));
359        instrument_expression(node->pn_right, f);        instrument_expression(node->pn_right, f);
360        break;        break;
361      case PN_LIST:      case PN_LIST:
362        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) {
363          if (p != node->pn_head) {          if (p != node->pn_head) {
364            fprintf(f, " %s ", get_op(node->pn_op));            Stream_printf(f, " %s ", get_op(node->pn_op));
365          }          }
366          instrument_expression(p, f);          instrument_expression(p, f);
367        }        }
# Line 307  Line 373 
373    case TOK_UNARYOP:    case TOK_UNARYOP:
374      switch (node->pn_op) {      switch (node->pn_op) {
375      case JSOP_NEG:      case JSOP_NEG:
376        fputc('-', f);        Stream_write_char(f, '-');
377        instrument_expression(node->pn_kid, f);        instrument_expression(node->pn_kid, f);
378        break;        break;
379      case JSOP_POS:      case JSOP_POS:
380        fputc('+', f);        Stream_write_char(f, '+');
381        instrument_expression(node->pn_kid, f);        instrument_expression(node->pn_kid, f);
382        break;        break;
383      case JSOP_NOT:      case JSOP_NOT:
384        fputc('!', f);        Stream_write_char(f, '!');
385        instrument_expression(node->pn_kid, f);        instrument_expression(node->pn_kid, f);
386        break;        break;
387      case JSOP_BITNOT:      case JSOP_BITNOT:
388        fputc('~', f);        Stream_write_char(f, '~');
389        instrument_expression(node->pn_kid, f);        instrument_expression(node->pn_kid, f);
390        break;        break;
391      case JSOP_TYPEOF:      case JSOP_TYPEOF:
392        fprintf(f, "typeof ");        Stream_write_string(f, "typeof ");
393        instrument_expression(node->pn_kid, f);        instrument_expression(node->pn_kid, f);
394        break;        break;
395      case JSOP_VOID:      case JSOP_VOID:
396        fprintf(f, "void ");        Stream_write_string(f, "void ");
397        instrument_expression(node->pn_kid, f);        instrument_expression(node->pn_kid, f);
398        break;        break;
399      default:      default:
# Line 344  Line 410 
410      case JSOP_INCNAME:      case JSOP_INCNAME:
411      case JSOP_INCPROP:      case JSOP_INCPROP:
412      case JSOP_INCELEM:      case JSOP_INCELEM:
413        fprintf(f, "++");        Stream_write_string(f, "++");
414        instrument_expression(node->pn_kid, f);        instrument_expression(node->pn_kid, f);
415        break;        break;
416      case JSOP_DECNAME:      case JSOP_DECNAME:
417      case JSOP_DECPROP:      case JSOP_DECPROP:
418      case JSOP_DECELEM:      case JSOP_DECELEM:
419        fprintf(f, "--");        Stream_write_string(f, "--");
420        instrument_expression(node->pn_kid, f);        instrument_expression(node->pn_kid, f);
421        break;        break;
422      case JSOP_NAMEINC:      case JSOP_NAMEINC:
423      case JSOP_PROPINC:      case JSOP_PROPINC:
424      case JSOP_ELEMINC:      case JSOP_ELEMINC:
425        instrument_expression(node->pn_kid, f);        instrument_expression(node->pn_kid, f);
426        fprintf(f, "++");        Stream_write_string(f, "++");
427        break;        break;
428      case JSOP_NAMEDEC:      case JSOP_NAMEDEC:
429      case JSOP_PROPDEC:      case JSOP_PROPDEC:
430      case JSOP_ELEMDEC:      case JSOP_ELEMDEC:
431        instrument_expression(node->pn_kid, f);        instrument_expression(node->pn_kid, f);
432        fprintf(f, "--");        Stream_write_string(f, "--");
433        break;        break;
434      default:      default:
435        abort();        abort();
# Line 371  Line 437 
437      }      }
438      break;      break;
439    case TOK_NEW:    case TOK_NEW:
440      fprintf(f, "new ");      Stream_write_string(f, "new ");
441      instrument_function_call(node, f);      instrument_function_call(node, f);
442      break;      break;
443    case TOK_DELETE:    case TOK_DELETE:
444      fprintf(f, "delete ");      Stream_write_string(f, "delete ");
445      instrument_expression(node->pn_kid, f);      instrument_expression(node->pn_kid, f);
446      break;      break;
447    case TOK_DOT:    case TOK_DOT:
# Line 385  Line 451 
451      the dot syntax.      the dot syntax.
452      */      */
453      instrument_expression(node->pn_expr, f);      instrument_expression(node->pn_expr, f);
454      /*      assert(ATOM_IS_STRING(node->pn_atom));
455      fputc('.', f);      {
456      print_string_atom(node->pn_atom, f);        JSString * s = ATOM_TO_STRING(node->pn_atom);
457      */        /* XXX - semantics changed in 1.7 */
458      fputc('[', f);        if (! ATOM_KEYWORD(node->pn_atom) && js_IsIdentifier(s)) {
459      print_quoted_string_atom(node->pn_atom, f);          Stream_write_char(f, '.');
460      fputc(']', f);          print_string_atom(node->pn_atom, f);
461          }
462          else {
463            Stream_write_char(f, '[');
464            print_quoted_string_atom(node->pn_atom, f);
465            Stream_write_char(f, ']');
466          }
467        }
468      break;      break;
469    case TOK_LB:    case TOK_LB:
470      instrument_expression(node->pn_left, f);      instrument_expression(node->pn_left, f);
471      fputc('[', f);      Stream_write_char(f, '[');
472      instrument_expression(node->pn_right, f);      instrument_expression(node->pn_right, f);
473      fputc(']', f);      Stream_write_char(f, ']');
474      break;      break;
475    case TOK_LP:    case TOK_LP:
476      instrument_function_call(node, f);      instrument_function_call(node, f);
477      break;      break;
478    case TOK_RB:    case TOK_RB:
479      fputc('[', f);      Stream_write_char(f, '[');
480      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) {
481        if (p != node->pn_head) {        if (p != node->pn_head) {
482          fprintf(f, ", ");          Stream_write_string(f, ", ");
483        }        }
484        /* TOK_COMMA is a special case: a hole in the array */        /* TOK_COMMA is a special case: a hole in the array */
485        if (p->pn_type != TOK_COMMA) {        if (p->pn_type != TOK_COMMA) {
# Line 414  Line 487 
487        }        }
488      }      }
489      if (node->pn_extra == PNX_ENDCOMMA) {      if (node->pn_extra == PNX_ENDCOMMA) {
490        fputc(',', f);        Stream_write_char(f, ',');
491      }      }
492      fputc(']', f);      Stream_write_char(f, ']');
493      break;      break;
494    case TOK_RC:    case TOK_RC:
495      fputc('{', f);      Stream_write_char(f, '{');
496      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) {
497        assert(p->pn_type == TOK_COLON);        assert(p->pn_type == TOK_COLON);
498        if (p != node->pn_head) {        if (p != node->pn_head) {
499          fprintf(f, ", ");          Stream_write_string(f, ", ");
500          }
501    
502          /* check whether this is a getter or setter */
503          switch (p->pn_op) {
504          case JSOP_GETTER:
505            Stream_write_string(f, "get ");
506            instrument_expression(p->pn_left, f);
507            instrument_function(p->pn_right, f, 0, FUNCTION_GETTER_OR_SETTER);
508            break;
509          case JSOP_SETTER:
510            Stream_write_string(f, "set ");
511            instrument_expression(p->pn_left, f);
512            instrument_function(p->pn_right, f, 0, FUNCTION_GETTER_OR_SETTER);
513            break;
514          default:
515            instrument_expression(p->pn_left, f);
516            Stream_write_string(f, ": ");
517            instrument_expression(p->pn_right, f);
518            break;
519        }        }
       instrument_expression(p->pn_left, f);  
       fprintf(f, ": ");  
       instrument_expression(p->pn_right, f);  
520      }      }
521      fputc('}', f);      Stream_write_char(f, '}');
522      break;      break;
523    case TOK_RP:    case TOK_RP:
524      fputc('(', f);      Stream_write_char(f, '(');
525      instrument_expression(node->pn_kid, f);      instrument_expression(node->pn_kid, f);
526      fputc(')', f);      Stream_write_char(f, ')');
527      break;      break;
528    case TOK_NAME:    case TOK_NAME:
529      print_string_atom(node->pn_atom, f);      print_string_atom(node->pn_atom, f);
# Line 454  Line 543 
543          JSObject * object = ATOM_TO_OBJECT(node->pn_atom);          JSObject * object = ATOM_TO_OBJECT(node->pn_atom);
544          jsval result;          jsval result;
545          js_regexp_toString(context, object, 0, NULL, &result);          js_regexp_toString(context, object, 0, NULL, &result);
546          print_string_jsval(result, f);          print_regex(result, f);
547        }        }
548        break;        break;
549      default:      default:
# Line 470  Line 559 
559      To keep the output simple, special-case zero.      To keep the output simple, special-case zero.
560      */      */
561      if (node->pn_dval == 0.0) {      if (node->pn_dval == 0.0) {
562        fprintf(f, "0");        Stream_write_string(f, "0");
563      }      }
564      else {      else {
565        fprintf(f, "%.15g", node->pn_dval);        Stream_printf(f, "%.15g", node->pn_dval);
566      }      }
567      break;      break;
568    case TOK_PRIMARY:    case TOK_PRIMARY:
569      switch (node->pn_op) {      switch (node->pn_op) {
570      case JSOP_TRUE:      case JSOP_TRUE:
571        fprintf(f, "true");        Stream_write_string(f, "true");
572        break;        break;
573      case JSOP_FALSE:      case JSOP_FALSE:
574        fprintf(f, "false");        Stream_write_string(f, "false");
575        break;        break;
576      case JSOP_NULL:      case JSOP_NULL:
577        fprintf(f, "null");        Stream_write_string(f, "null");
578        break;        break;
579      case JSOP_THIS:      case JSOP_THIS:
580        fprintf(f, "this");        Stream_write_string(f, "this");
581        break;        break;
582      /* jsscan.h mentions `super' ??? */      /* jsscan.h mentions `super' ??? */
583      default:      default:
# Line 497  Line 586 
586      break;      break;
587    case TOK_INSTANCEOF:    case TOK_INSTANCEOF:
588      instrument_expression(node->pn_left, f);      instrument_expression(node->pn_left, f);
589      fprintf(f, " instanceof ");      Stream_write_string(f, " instanceof ");
590      instrument_expression(node->pn_right, f);      instrument_expression(node->pn_right, f);
591      break;      break;
592    case TOK_IN:    case TOK_IN:
593      instrument_expression(node->pn_left, f);      instrument_expression(node->pn_left, f);
594      fprintf(f, " in ");      Stream_write_string(f, " in ");
595      instrument_expression(node->pn_right, f);      instrument_expression(node->pn_right, f);
596      break;      break;
597    default:    default:
# Line 510  Line 599 
599    }    }
600  }  }
601    
602  static void instrument_var_statement(JSParseNode * node, FILE * f, int indent) {  static void instrument_var_statement(JSParseNode * node, Stream * f, int indent) {
603    assert(node->pn_arity == PN_LIST);    assert(node->pn_arity == PN_LIST);
604    fprintf(f, "%*s", indent, "");    Stream_printf(f, "%*s", indent, "");
605    fprintf(f, "var ");    Stream_write_string(f, "var ");
606    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) {
607      assert(p->pn_type == TOK_NAME);      assert(p->pn_type == TOK_NAME);
608      assert(p->pn_arity == PN_NAME);      assert(p->pn_arity == PN_NAME);
609      if (p != node->pn_head) {      if (p != node->pn_head) {
610        fprintf(f, ", ");        Stream_write_string(f, ", ");
611      }      }
612      print_string_atom(p->pn_atom, f);      print_string_atom(p->pn_atom, f);
613      if (p->pn_expr != NULL) {      if (p->pn_expr != NULL) {
614        fprintf(f, " = ");        Stream_write_string(f, " = ");
615        instrument_expression(p->pn_expr, f);        instrument_expression(p->pn_expr, f);
616      }      }
617    }    }
618  }  }
619    
620  static void output_statement(JSParseNode * node, FILE * f, int indent) {  static void output_statement(JSParseNode * node, Stream * f, int indent) {
621    switch (node->pn_type) {    switch (node->pn_type) {
622    case TOK_FUNCTION:    case TOK_FUNCTION:
623      instrument_function(node, f, indent);      instrument_function(node, f, indent, FUNCTION_NORMAL);
624      break;      break;
625    case TOK_LC:    case TOK_LC:
626      assert(node->pn_arity == PN_LIST);      assert(node->pn_arity == PN_LIST);
627  /*  /*
628      fprintf(f, "{\n");      Stream_write_string(f, "{\n");
629  */  */
630      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) {
631        instrument_statement(p, f, indent);        instrument_statement(p, f, indent);
632      }      }
633  /*  /*
634      fprintf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
635      fprintf(f, "}\n");      Stream_write_string(f, "}\n");
636  */  */
637      break;      break;
638    case TOK_IF:    case TOK_IF:
639      assert(node->pn_arity == PN_TERNARY);      assert(node->pn_arity == PN_TERNARY);
640      fprintf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
641      fprintf(f, "if (");      Stream_write_string(f, "if (");
642      instrument_expression(node->pn_kid1, f);      instrument_expression(node->pn_kid1, f);
643      fprintf(f, ") {\n");      Stream_write_string(f, ") {\n");
644      instrument_statement(node->pn_kid2, f, indent + 2);      instrument_statement(node->pn_kid2, f, indent + 2);
645      fprintf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
646      fprintf(f, "}\n");      Stream_write_string(f, "}\n");
647      if (node->pn_kid3) {      if (node->pn_kid3) {
648        fprintf(f, "%*s", indent, "");        Stream_printf(f, "%*s", indent, "");
649        fprintf(f, "else {\n");        Stream_write_string(f, "else {\n");
650        instrument_statement(node->pn_kid3, f, indent + 2);        instrument_statement(node->pn_kid3, f, indent + 2);
651        fprintf(f, "%*s", indent, "");        Stream_printf(f, "%*s", indent, "");
652        fprintf(f, "}\n");        Stream_write_string(f, "}\n");
653      }      }
654      break;      break;
655    case TOK_SWITCH:    case TOK_SWITCH:
656      assert(node->pn_arity == PN_BINARY);      assert(node->pn_arity == PN_BINARY);
657      fprintf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
658      fprintf(f, "switch (");      Stream_write_string(f, "switch (");
659      instrument_expression(node->pn_left, f);      instrument_expression(node->pn_left, f);
660      fprintf(f, ") {\n");      Stream_write_string(f, ") {\n");
661      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) {
662        fprintf(f, "%*s", indent, "");        Stream_printf(f, "%*s", indent, "");
663        switch (p->pn_type) {        switch (p->pn_type) {
664        case TOK_CASE:        case TOK_CASE:
665          fprintf(f, "case ");          Stream_write_string(f, "case ");
666          instrument_expression(p->pn_left, f);          instrument_expression(p->pn_left, f);
667          fprintf(f, ":\n");          Stream_write_string(f, ":\n");
668          break;          break;
669        case TOK_DEFAULT:        case TOK_DEFAULT:
670          fprintf(f, "default:\n");          Stream_write_string(f, "default:\n");
671          break;          break;
672        default:        default:
673          abort();          abort();
# Line 586  Line 675 
675        }        }
676        instrument_statement(p->pn_right, f, indent + 2);        instrument_statement(p->pn_right, f, indent + 2);
677      }      }
678      fprintf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
679      fprintf(f, "}\n");      Stream_write_string(f, "}\n");
680      break;      break;
681    case TOK_CASE:    case TOK_CASE:
682    case TOK_DEFAULT:    case TOK_DEFAULT:
# Line 595  Line 684 
684      break;      break;
685    case TOK_WHILE:    case TOK_WHILE:
686      assert(node->pn_arity == PN_BINARY);      assert(node->pn_arity == PN_BINARY);
687      fprintf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
688      fprintf(f, "while (");      Stream_write_string(f, "while (");
689      instrument_expression(node->pn_left, f);      instrument_expression(node->pn_left, f);
690      fprintf(f, ") {\n");      Stream_write_string(f, ") {\n");
691      instrument_statement(node->pn_right, f, indent + 2);      instrument_statement(node->pn_right, f, indent + 2);
692      fprintf(f, "}\n");      Stream_write_string(f, "}\n");
693      break;      break;
694    case TOK_DO:    case TOK_DO:
695      assert(node->pn_arity == PN_BINARY);      assert(node->pn_arity == PN_BINARY);
696      fprintf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
697      fprintf(f, "do {\n");      Stream_write_string(f, "do {\n");
698      instrument_statement(node->pn_left, f, indent + 2);      instrument_statement(node->pn_left, f, indent + 2);
699      fprintf(f, "}\n");      Stream_write_string(f, "}\n");
700      fprintf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
701      fprintf(f, "while (");      Stream_write_string(f, "while (");
702      instrument_expression(node->pn_right, f);      instrument_expression(node->pn_right, f);
703      fprintf(f, ");\n");      Stream_write_string(f, ");\n");
704      break;      break;
705    case TOK_FOR:    case TOK_FOR:
706      assert(node->pn_arity == PN_BINARY);      assert(node->pn_arity == PN_BINARY);
707      fprintf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
708      fprintf(f, "for (");      Stream_write_string(f, "for (");
709      switch (node->pn_left->pn_type) {      switch (node->pn_left->pn_type) {
710      case TOK_IN:      case TOK_IN:
711        /* for/in */        /* for/in */
# Line 639  Line 728 
728          break;          break;
729  */  */
730        }        }
731        fprintf(f, " in ");        Stream_write_string(f, " in ");
732        instrument_expression(node->pn_left->pn_right, f);        instrument_expression(node->pn_left->pn_right, f);
733        break;        break;
734      case TOK_RESERVED:      case TOK_RESERVED:
# Line 653  Line 742 
742            instrument_expression(node->pn_left->pn_kid1, f);            instrument_expression(node->pn_left->pn_kid1, f);
743          }          }
744        }        }
745        fprintf(f, ";");        Stream_write_string(f, ";");
746        if (node->pn_left->pn_kid2) {        if (node->pn_left->pn_kid2) {
747          fputc(' ', f);          Stream_write_char(f, ' ');
748          instrument_expression(node->pn_left->pn_kid2, f);          instrument_expression(node->pn_left->pn_kid2, f);
749        }        }
750        fprintf(f, ";");        Stream_write_string(f, ";");
751        if (node->pn_left->pn_kid3) {        if (node->pn_left->pn_kid3) {
752          fputc(' ', f);          Stream_write_char(f, ' ');
753          instrument_expression(node->pn_left->pn_kid3, f);          instrument_expression(node->pn_left->pn_kid3, f);
754        }        }
755        break;        break;
# Line 668  Line 757 
757        abort();        abort();
758        break;        break;
759      }      }
760      fprintf(f, ") {\n");      Stream_write_string(f, ") {\n");
761      instrument_statement(node->pn_right, f, indent + 2);      instrument_statement(node->pn_right, f, indent + 2);
762      fprintf(f, "}\n");      Stream_write_string(f, "}\n");
763      break;      break;
764    case TOK_THROW:    case TOK_THROW:
765      assert(node->pn_arity == PN_UNARY);      assert(node->pn_arity == PN_UNARY);
766      fprintf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
767      fprintf(f, "throw ");      Stream_write_string(f, "throw ");
768      instrument_expression(node->pn_u.unary.kid, f);      instrument_expression(node->pn_u.unary.kid, f);
769      fprintf(f, ";\n");      Stream_write_string(f, ";\n");
770      break;      break;
771    case TOK_TRY:    case TOK_TRY:
772      fprintf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
773      fprintf(f, "try {\n");      Stream_write_string(f, "try {\n");
774      instrument_statement(node->pn_kid1, f, indent + 2);      instrument_statement(node->pn_kid1, f, indent + 2);
775      fprintf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
776      fprintf(f, "}\n");      Stream_write_string(f, "}\n");
777      {      {
778        for (JSParseNode * catch = node->pn_kid2; catch != NULL; catch = catch->pn_kid2) {        for (JSParseNode * catch = node->pn_kid2; catch != NULL; catch = catch->pn_kid2) {
779          assert(catch->pn_type == TOK_CATCH);          assert(catch->pn_type == TOK_CATCH);
780          fprintf(f, "%*s", indent, "");          Stream_printf(f, "%*s", indent, "");
781          fprintf(f, "catch (");          Stream_write_string(f, "catch (");
782          assert(catch->pn_kid1->pn_arity == PN_NAME);          assert(catch->pn_kid1->pn_arity == PN_NAME);
783          print_string_atom(catch->pn_kid1->pn_atom, f);          print_string_atom(catch->pn_kid1->pn_atom, f);
784          if (catch->pn_kid1->pn_expr) {          if (catch->pn_kid1->pn_expr) {
785            fprintf(f, " if ");            Stream_write_string(f, " if ");
786            instrument_expression(catch->pn_kid1->pn_expr, f);            instrument_expression(catch->pn_kid1->pn_expr, f);
787          }          }
788          fprintf(f, ") {\n");          Stream_write_string(f, ") {\n");
789          instrument_statement(catch->pn_kid3, f, indent + 2);          instrument_statement(catch->pn_kid3, f, indent + 2);
790          fprintf(f, "%*s", indent, "");          Stream_printf(f, "%*s", indent, "");
791          fprintf(f, "}\n");          Stream_write_string(f, "}\n");
792        }        }
793      }      }
794      if (node->pn_kid3) {      if (node->pn_kid3) {
795        fprintf(f, "%*s", indent, "");        Stream_printf(f, "%*s", indent, "");
796        fprintf(f, "finally {\n");        Stream_write_string(f, "finally {\n");
797        instrument_statement(node->pn_kid3, f, indent + 2);        instrument_statement(node->pn_kid3, f, indent + 2);
798        fprintf(f, "%*s", indent, "");        Stream_printf(f, "%*s", indent, "");
799        fprintf(f, "}\n");        Stream_write_string(f, "}\n");
800      }      }
801      break;      break;
802    case TOK_CATCH:    case TOK_CATCH:
# Line 716  Line 805 
805    case TOK_BREAK:    case TOK_BREAK:
806    case TOK_CONTINUE:    case TOK_CONTINUE:
807      assert(node->pn_arity == PN_NAME || node->pn_arity == PN_NULLARY);      assert(node->pn_arity == PN_NAME || node->pn_arity == PN_NULLARY);
808      fprintf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
809      fputs(node->pn_type == TOK_BREAK? "break": "continue", f);      Stream_write_string(f, node->pn_type == TOK_BREAK? "break": "continue");
810      JSAtom * atom = node->pn_u.name.atom;      JSAtom * atom = node->pn_u.name.atom;
811      if (atom != NULL) {      if (atom != NULL) {
812        fputc(' ', f);        Stream_write_char(f, ' ');
813        print_string_atom(node->pn_atom, f);        print_string_atom(node->pn_atom, f);
814      }      }
815      fprintf(f, ";\n");      Stream_write_string(f, ";\n");
816      break;      break;
817    case TOK_WITH:    case TOK_WITH:
818      assert(node->pn_arity == PN_BINARY);      assert(node->pn_arity == PN_BINARY);
819      fprintf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
820      fprintf(f, "with (");      Stream_write_string(f, "with (");
821      instrument_expression(node->pn_left, f);      instrument_expression(node->pn_left, f);
822      fprintf(f, ") {\n");      Stream_write_string(f, ") {\n");
823      instrument_statement(node->pn_right, f, indent + 2);      instrument_statement(node->pn_right, f, indent + 2);
824      fprintf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
825      fprintf(f, "}\n");      Stream_write_string(f, "}\n");
826      break;      break;
827    case TOK_VAR:    case TOK_VAR:
828      instrument_var_statement(node, f, indent);      instrument_var_statement(node, f, indent);
829      fprintf(f, ";\n");      Stream_write_string(f, ";\n");
830      break;      break;
831    case TOK_RETURN:    case TOK_RETURN:
832      assert(node->pn_arity == PN_UNARY);      assert(node->pn_arity == PN_UNARY);
833      fprintf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
834      fprintf(f, "return");      Stream_write_string(f, "return");
835      if (node->pn_kid != NULL) {      if (node->pn_kid != NULL) {
836        fprintf(f, " ");        Stream_write_char(f, ' ');
837        instrument_expression(node->pn_kid, f);        instrument_expression(node->pn_kid, f);
838      }      }
839      fprintf(f, ";\n");      Stream_write_string(f, ";\n");
840      break;      break;
841    case TOK_SEMI:    case TOK_SEMI:
842      assert(node->pn_arity == PN_UNARY);      assert(node->pn_arity == PN_UNARY);
843      fprintf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
844      if (node->pn_kid != NULL) {      if (node->pn_kid != NULL) {
845        instrument_expression(node->pn_kid, f);        instrument_expression(node->pn_kid, f);
846      }      }
847      fprintf(f, ";\n");      Stream_write_string(f, ";\n");
848      break;      break;
849    case TOK_COLON:    case TOK_COLON:
850      assert(node->pn_arity == PN_NAME);      assert(node->pn_arity == PN_NAME);
# Line 763  Line 852 
852      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
853      statement it's supposed to label ...      statement it's supposed to label ...
854      */      */
855      fprintf(f, "%*s", indent < 2? 0: indent - 2, "");      Stream_printf(f, "%*s", indent < 2? 0: indent - 2, "");
856      print_string_atom(node->pn_atom, f);      print_string_atom(node->pn_atom, f);
857      fprintf(f, ":\n");      Stream_write_string(f, ":\n");
858      /*      /*
859      ... use output_statement instead of instrument_statement.      ... use output_statement instead of instrument_statement.
860      */      */
# Line 781  Line 870 
870  TOK_FUNCTION is handled as a statement and as an expression.  TOK_FUNCTION is handled as a statement and as an expression.
871  TOK_EXPORT, TOK_IMPORT are not handled.  TOK_EXPORT, TOK_IMPORT are not handled.
872  */  */
873  static void instrument_statement(JSParseNode * node, FILE * f, int indent) {  static void instrument_statement(JSParseNode * node, Stream * f, int indent) {
874    if (node->pn_type != TOK_LC) {    if (node->pn_type != TOK_LC) {
875      int line = node->pn_pos.begin.lineno;      int line = node->pn_pos.begin.lineno;
876      /* the root node has line number 0 */      /* the root node has line number 0 */
877      if (line != 0) {      if (line != 0) {
878        fprintf(f, "%*s", indent, "");        Stream_printf(f, "%*s", indent, "");
879        fprintf(f, "_$jscoverage['%s'][%d]++;\n", file_id, line);        Stream_printf(f, "_$jscoverage['%s'][%d]++;\n", file_id, line);
880        lines[line - 1] = 1;        lines[line - 1] = 1;
881      }      }
882    }    }
883    output_statement(node, f, indent);    output_statement(node, f, indent);
884  }  }
885    
886  static void instrument_js_stream(const char * id, int line, FILE * input, FILE * output, const char * temporary_file_name) {  void jscoverage_instrument_js(const char * id, const char * encoding, Stream * input, Stream * output) {
887    file_id = id;    file_id = id;
888    
889    /* scan the javascript */    /* scan the javascript */
890    JSTokenStream * token_stream = js_NewFileTokenStream(context, NULL, input);    size_t input_length = input->length;
891      jschar * base = NULL;
892      int result = jscoverage_bytes_to_characters(encoding, input->data, input->length, &base, &input_length);
893      if (result == JSCOVERAGE_ERROR_ENCODING_NOT_SUPPORTED) {
894        fatal("encoding %s not supported in file %s", encoding, id);
895      }
896      else if (result == JSCOVERAGE_ERROR_INVALID_BYTE_SEQUENCE) {
897        fatal("error decoding %s in file %s", encoding, id);
898      }
899      JSTokenStream * token_stream = js_NewTokenStream(context, base, input_length, NULL, 1, NULL);
900    if (token_stream == NULL) {    if (token_stream == NULL) {
901      fatal("cannot create token stream from file: %s", file_id);      fatal("cannot create token stream from file: %s", file_id);
902    }    }
# Line 815  Line 913 
913    }    }
914    
915    /*    /*
916    Create a temporary file - we can't write directly to the output because we    An instrumented JavaScript file has 3 sections:
917    need to know the line number info first.    1. initialization
918      2. instrumented source code
919      3. original source code
920    */    */
   FILE * temporary = fopen(temporary_file_name, "w+");  
   if (temporary == NULL) {  
     fatal("cannot create temporary file for script: %s", file_id);  
   }  
921    
922    /* write instrumented javascript to the temporary */    Stream * instrumented = Stream_new(0);
923    instrument_statement(node, temporary, 0);    instrument_statement(node, instrumented, 0);
924    
925    /* write line number info to the output */    /* write line number info to the output */
926    fprintf(output, "/* automatically generated by JSCoverage - do not edit */\n");    Stream_write_string(output, "/* automatically generated by JSCoverage - do not edit */\n");
927    fprintf(output, "if (! top._$jscoverage) {\n  top._$jscoverage = {};\n}\n");    Stream_write_string(output, "if (! top._$jscoverage) {\n  top._$jscoverage = {};\n}\n");
928    fprintf(output, "var _$jscoverage = top._$jscoverage;\n");    Stream_write_string(output, "var _$jscoverage = top._$jscoverage;\n");
929    fprintf(output, "if (! _$jscoverage['%s']) {\n", file_id);    Stream_printf(output, "if (! _$jscoverage['%s']) {\n", file_id);
930    fprintf(output, "  _$jscoverage['%s'] = [];\n", file_id);    Stream_printf(output, "  _$jscoverage['%s'] = [];\n", file_id);
931    for (int i = 0; i < num_lines; i++) {    for (int i = 0; i < num_lines; i++) {
932      if (lines[i]) {      if (lines[i]) {
933        fprintf(output, "  _$jscoverage['%s'][%d] = 0;\n", file_id, i + 1);        Stream_printf(output, "  _$jscoverage['%s'][%d] = 0;\n", file_id, i + 1);
934      }      }
935    }    }
936    fprintf(output, "}\n");    Stream_write_string(output, "}\n");
937      free(lines);
938    lines = NULL;    lines = NULL;
939    
940    /* copy the temporary to the output */    /* copy the instrumented source code to the output */
941    fseek(temporary, 0, SEEK_SET);    Stream_write(output, instrumented->data, instrumented->length);
942    copy_stream(temporary, output);    Stream_write_char(output, '\n');
943    
944      /* conditionals */
945      bool has_conditionals = false;
946      size_t line_number = 0;
947      size_t i = 0;
948      while (i < input_length) {
949        line_number++;
950        size_t line_start = i;
951        while (i < input_length && base[i] != '\r' && base[i] != '\n') {
952          i++;
953        }
954        size_t line_end = i;
955        if (i < input_length) {
956          if (base[i] == '\r') {
957            line_end = i;
958            i++;
959            if (i < input_length && base[i] == '\n') {
960              i++;
961            }
962          }
963          else if (base[i] == '\n') {
964            line_end = i;
965            i++;
966          }
967          else {
968            abort();
969          }
970        }
971        char * line = js_DeflateString(context, base + line_start, line_end - line_start);
972        if (str_starts_with(line, "//#JSCOVERAGE_IF")) {
973          if (! has_conditionals) {
974            has_conditionals = true;
975            Stream_printf(output, "_$jscoverage['%s'].conditionals = [];\n", file_id);
976          }
977          Stream_printf(output, "if (!%s) {\n", line + 16);
978          Stream_printf(output, "  _$jscoverage['%s'].conditionals[%d] = ", file_id, line_number);
979        }
980        else if (str_starts_with(line, "//#JSCOVERAGE_ENDIF")) {
981          Stream_printf(output, "%d;\n", line_number);
982          Stream_printf(output, "}\n");
983        }
984        JS_free(context, line);
985      }
986    
987      /* copy the original source to the output */
988      i = 0;
989      while (i < input_length) {
990        Stream_write_string(output, "// ");
991        size_t line_start = i;
992        while (i < input_length && base[i] != '\r' && base[i] != '\n') {
993          i++;
994        }
995    
996        size_t line_end = i;
997        if (i < input_length) {
998          if (base[i] == '\r') {
999            line_end = i;
1000            i++;
1001            if (i < input_length && base[i] == '\n') {
1002              i++;
1003            }
1004          }
1005          else if (base[i] == '\n') {
1006            line_end = i;
1007            i++;
1008          }
1009          else {
1010            abort();
1011          }
1012        }
1013    
1014        char * line = js_DeflateString(context, base + line_start, line_end - line_start);
1015        Stream_write_string(output, line);
1016        Stream_write_char(output, '\n');
1017        JS_free(context, line);
1018      }
1019    
1020      Stream_delete(instrumented);
1021    
1022    fclose(temporary);    JS_free(context, base);
1023    
1024    file_id = NULL;    file_id = NULL;
1025  }  }
1026    
1027  void jscoverage_instrument_js(const char * id, FILE * input, FILE * output, const char * temporary_file_name) {  void jscoverage_copy_resources(const char * destination_directory) {
1028    instrument_js_stream(id, 0, input, output, temporary_file_name);    copy_resource("jscoverage.html", destination_directory);
1029      copy_resource("jscoverage.css", destination_directory);
1030      copy_resource("jscoverage.js", destination_directory);
1031      copy_resource("jscoverage-ie.css", destination_directory);
1032      copy_resource("jscoverage-throbber.gif", destination_directory);
1033      copy_resource("jscoverage-sh_main.js", destination_directory);
1034      copy_resource("jscoverage-sh_javascript.js", destination_directory);
1035      copy_resource("jscoverage-sh_nedit.css", destination_directory);
1036    }
1037    
1038    /*
1039    coverage reports
1040    */
1041    
1042    struct FileCoverageList {
1043      FileCoverage * file_coverage;
1044      struct FileCoverageList * next;
1045    };
1046    
1047    struct Coverage {
1048      JSHashTable * coverage_table;
1049      struct FileCoverageList * coverage_list;
1050    };
1051    
1052    static int compare_strings(const void * p1, const void * p2) {
1053      return strcmp(p1, p2) == 0;
1054    }
1055    
1056    Coverage * Coverage_new(void) {
1057      Coverage * result = xmalloc(sizeof(Coverage));
1058      result->coverage_table = JS_NewHashTable(1024, JS_HashString, compare_strings, NULL, NULL, NULL);
1059      if (result->coverage_table == NULL) {
1060        fatal("cannot create hash table");
1061      }
1062      result->coverage_list = NULL;
1063      return result;
1064    }
1065    
1066    void Coverage_delete(Coverage * coverage) {
1067      JS_HashTableDestroy(coverage->coverage_table);
1068      struct FileCoverageList * p = coverage->coverage_list;
1069      while (p != NULL) {
1070        free(p->file_coverage->lines);
1071        free(p->file_coverage->source);
1072        free(p->file_coverage->id);
1073        free(p->file_coverage);
1074        struct FileCoverageList * q = p;
1075        p = p->next;
1076        free(q);
1077      }
1078      free(coverage);
1079    }
1080    
1081    struct EnumeratorArg {
1082      CoverageForeachFunction f;
1083      void * p;
1084    };
1085    
1086    static intN enumerator(JSHashEntry * entry, intN i, void * arg) {
1087      struct EnumeratorArg * enumerator_arg = arg;
1088      enumerator_arg->f(entry->value, i, enumerator_arg->p);
1089      return 0;
1090    }
1091    
1092    void Coverage_foreach_file(Coverage * coverage, CoverageForeachFunction f, void * p) {
1093      struct EnumeratorArg enumerator_arg;
1094      enumerator_arg.f = f;
1095      enumerator_arg.p = p;
1096      JS_HashTableEnumerateEntries(coverage->coverage_table, enumerator, &enumerator_arg);
1097    }
1098    
1099    int jscoverage_parse_json(Coverage * coverage, const uint8_t * json, size_t length) {
1100      jschar * base = js_InflateString(context, (char *) json, &length);
1101      if (base == NULL) {
1102        fatal("out of memory");
1103      }
1104    
1105      jschar * parenthesized_json = xnew(jschar, addst(length, 2));
1106      parenthesized_json[0] = '(';
1107      memcpy(parenthesized_json + 1, base, mulst(length, sizeof(jschar)));
1108      parenthesized_json[length + 1] = ')';
1109    
1110      JS_free(context, base);
1111    
1112      JSTokenStream * token_stream = js_NewTokenStream(context, parenthesized_json, length + 2, NULL, 1, NULL);
1113      if (token_stream == NULL) {
1114        fatal("cannot create token stream");
1115      }
1116    
1117      JSParseNode * root = js_ParseTokenStream(context, global, token_stream);
1118      free(parenthesized_json);
1119      if (root == NULL) {
1120        return -1;
1121      }
1122    
1123      /* root node must be TOK_LC */
1124      if (root->pn_type != TOK_LC) {
1125        return -1;
1126      }
1127      JSParseNode * semi = root->pn_u.list.head;
1128    
1129      /* the list must be TOK_SEMI and it must contain only one element */
1130      if (semi->pn_type != TOK_SEMI || semi->pn_next != NULL) {
1131        return -1;
1132      }
1133      JSParseNode * parenthesized = semi->pn_kid;
1134    
1135      /* this must be a parenthesized expression */
1136      if (parenthesized->pn_type != TOK_RP) {
1137        return -1;
1138      }
1139      JSParseNode * object = parenthesized->pn_kid;
1140    
1141      /* this must be an object literal */
1142      if (object->pn_type != TOK_RC) {
1143        return -1;
1144      }
1145    
1146      for (JSParseNode * p = object->pn_head; p != NULL; p = p->pn_next) {
1147        /* every element of this list must be TOK_COLON */
1148        if (p->pn_type != TOK_COLON) {
1149          return -1;
1150        }
1151    
1152        /* the key must be a string representing the file */
1153        JSParseNode * key = p->pn_left;
1154        if (key->pn_type != TOK_STRING || ! ATOM_IS_STRING(key->pn_atom)) {
1155          return -1;
1156        }
1157        char * id_bytes = JS_GetStringBytes(ATOM_TO_STRING(key->pn_atom));
1158    
1159        /* the value must be an object literal OR an array */
1160        JSParseNode * value = p->pn_right;
1161        if (! (value->pn_type == TOK_RC || value->pn_type == TOK_RB)) {
1162          return -1;
1163        }
1164    
1165        JSParseNode * array = NULL;
1166        JSParseNode * source = NULL;
1167        if (value->pn_type == TOK_RB) {
1168          /* an array */
1169          array = value;
1170        }
1171        else if (value->pn_type == TOK_RC) {
1172          /* an object literal */
1173          if (value->pn_count != 2) {
1174            return -1;
1175          }
1176          for (JSParseNode * element = value->pn_head; element != NULL; element = element->pn_next) {
1177            if (element->pn_type != TOK_COLON) {
1178              return -1;
1179            }
1180            JSParseNode * left = element->pn_left;
1181            if (left->pn_type != TOK_STRING || ! ATOM_IS_STRING(left->pn_atom)) {
1182              return -1;
1183            }
1184            const char * s = JS_GetStringBytes(ATOM_TO_STRING(left->pn_atom));
1185            if (strcmp(s, "coverage") == 0) {
1186              array = element->pn_right;
1187              if (array->pn_type != TOK_RB) {
1188                return -1;
1189              }
1190            }
1191            else if (strcmp(s, "source") == 0) {
1192              source = element->pn_right;
1193              if (source->pn_type != TOK_STRING || ! ATOM_IS_STRING(source->pn_atom)) {
1194                return -1;
1195              }
1196            }
1197            else {
1198              return -1;
1199            }
1200          }
1201        }
1202        else {
1203          return -1;
1204        }
1205    
1206        if (array == NULL) {
1207          return -1;
1208        }
1209    
1210        /* look up the file in the coverage table */
1211        FileCoverage * file_coverage = JS_HashTableLookup(coverage->coverage_table, id_bytes);
1212        if (file_coverage == NULL) {
1213          /* not there: create a new one */
1214          char * id = xstrdup(id_bytes);
1215          file_coverage = xmalloc(sizeof(FileCoverage));
1216          file_coverage->id = id;
1217          file_coverage->num_lines = array->pn_count - 1;
1218          file_coverage->lines = xnew(int, array->pn_count);
1219          if (source == NULL) {
1220            file_coverage->source = NULL;
1221          }
1222          else {
1223            file_coverage->source = xstrdup(JS_GetStringBytes(ATOM_TO_STRING(source->pn_atom)));
1224          }
1225    
1226          /* set coverage for all lines */
1227          uint32 i = 0;
1228          for (JSParseNode * element = array->pn_head; element != NULL; element = element->pn_next, i++) {
1229            if (element->pn_type == TOK_NUMBER) {
1230              file_coverage->lines[i] = (int) element->pn_dval;
1231            }
1232            else if (element->pn_type == TOK_PRIMARY && element->pn_op == JSOP_NULL) {
1233              file_coverage->lines[i] = -1;
1234            }
1235            else {
1236              return -1;
1237            }
1238          }
1239          assert(i == array->pn_count);
1240    
1241          /* add to the hash table */
1242          JS_HashTableAdd(coverage->coverage_table, id, file_coverage);
1243          struct FileCoverageList * coverage_list = xmalloc(sizeof(struct FileCoverageList));
1244          coverage_list->file_coverage = file_coverage;
1245          coverage_list->next = coverage->coverage_list;
1246          coverage->coverage_list = coverage_list;
1247        }
1248        else {
1249          /* sanity check */
1250          assert(strcmp(file_coverage->id, id_bytes) == 0);
1251          if (file_coverage->num_lines != array->pn_count - 1) {
1252            return -2;
1253          }
1254    
1255          /* merge the coverage */
1256          uint32 i = 0;
1257          for (JSParseNode * element = array->pn_head; element != NULL; element = element->pn_next, i++) {
1258            if (element->pn_type == TOK_NUMBER) {
1259              if (file_coverage->lines[i] == -1) {
1260                return -2;
1261              }
1262              file_coverage->lines[i] += (int) element->pn_dval;
1263            }
1264            else if (element->pn_type == TOK_PRIMARY && element->pn_op == JSOP_NULL) {
1265              if (file_coverage->lines[i] != -1) {
1266                return -2;
1267              }
1268            }
1269            else {
1270              return -1;
1271            }
1272          }
1273          assert(i == array->pn_count);
1274    
1275          /* if this JSON file has source, use it */
1276          if (file_coverage->source == NULL && source != NULL) {
1277            file_coverage->source = xstrdup(JS_GetStringBytes(ATOM_TO_STRING(source->pn_atom)));
1278          }
1279        }
1280      }
1281    
1282      return 0;
1283  }  }

Legend:
Removed from v.70  
changed lines
  Added in v.174

  ViewVC Help
Powered by ViewVC 1.1.24