/[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 159 by siliconforks, Sat Sep 13 04:47:17 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 "resource-manager.h"
38  #include "util.h"  #include "util.h"
39    
40  static JSRuntime * runtime = NULL;  static JSRuntime * runtime = NULL;
# Line 71  Line 74 
74    JS_DestroyRuntime(runtime);    JS_DestroyRuntime(runtime);
75  }  }
76    
77  static void print_string(JSString * s, FILE * f) {  static void print_string(JSString * s, Stream * f) {
78    for (int i = 0; i < s->length; i++) {    for (size_t i = 0; i < s->length; i++) {
79      char c = s->chars[i];      char c = s->chars[i];
80      fputc(c, f);      Stream_write_char(f, c);
81    }    }
82  }  }
83    
84  static void print_string_atom(JSAtom * atom, FILE * f) {  static void print_string_atom(JSAtom * atom, Stream * f) {
85    assert(ATOM_IS_STRING(atom));    assert(ATOM_IS_STRING(atom));
86    JSString * s = ATOM_TO_STRING(atom);    JSString * s = ATOM_TO_STRING(atom);
87    print_string(s, f);    print_string(s, f);
88  }  }
89    
90  static void print_string_jsval(jsval value, FILE * f) {  static void print_string_jsval(jsval value, Stream * f) {
91    assert(JSVAL_IS_STRING(value));    assert(JSVAL_IS_STRING(value));
92    JSString * s = JSVAL_TO_STRING(value);    JSString * s = JSVAL_TO_STRING(value);
93    print_string(s, f);    print_string(s, f);
94  }  }
95    
96  static void print_quoted_string_atom(JSAtom * atom, FILE * f) {  static void print_quoted_string_atom(JSAtom * atom, Stream * f) {
97    assert(ATOM_IS_STRING(atom));    assert(ATOM_IS_STRING(atom));
98    JSString * s = ATOM_TO_STRING(atom);    JSString * s = ATOM_TO_STRING(atom);
99    JSString * quoted = js_QuoteString(context, s, '"');    JSString * quoted = js_QuoteString(context, s, '"');
# Line 142  Line 145 
145    }    }
146  }  }
147    
148  static void instrument_expression(JSParseNode * node, FILE * f);  static void instrument_expression(JSParseNode * node, Stream * f);
149  static void instrument_statement(JSParseNode * node, FILE * f, int indent);  static void instrument_statement(JSParseNode * node, Stream * f, int indent);
150    
151  static void instrument_function(JSParseNode * node, FILE * f, int indent) {  enum FunctionType {
152      assert(node->pn_arity == PN_FUNC);    FUNCTION_NORMAL,
153      assert(ATOM_IS_OBJECT(node->pn_funAtom));    FUNCTION_GETTER_OR_SETTER
154      JSObject * object = ATOM_TO_OBJECT(node->pn_funAtom);  };
155      assert(JS_ObjectIsFunction(context, object));  
156      JSFunction * function = (JSFunction *) JS_GetPrivate(context, object);  static void instrument_function(JSParseNode * node, Stream * f, int indent, enum FunctionType type) {
157      assert(function);    assert(node->pn_arity == PN_FUNC);
158      assert(object == function->object);    assert(ATOM_IS_OBJECT(node->pn_funAtom));
159      fprintf(f, "%*s", indent, "");    JSObject * object = ATOM_TO_OBJECT(node->pn_funAtom);
160      fprintf(f, "function");    assert(JS_ObjectIsFunction(context, object));
161      JSFunction * function = (JSFunction *) JS_GetPrivate(context, object);
162      /* function name */    assert(function);
163      if (function->atom) {    assert(object == function->object);
164        fputc(' ', f);    Stream_printf(f, "%*s", indent, "");
165        print_string_atom(function->atom, f);    if (type == FUNCTION_NORMAL) {
166      }      Stream_write_string(f, "function");
167      }
     /* 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);  
168    
169      /* function body */    /* function name */
170      instrument_statement(node->pn_body, f, indent + 2);    if (function->atom) {
171        Stream_write_char(f, ' ');
172        print_string_atom(function->atom, f);
173      }
174    
175      fprintf(f, "}\n");    /* function parameters */
176      Stream_write_string(f, "(");
177      JSAtom ** params = xnew(JSAtom *, function->nargs);
178      for (int i = 0; i < function->nargs; i++) {
179        /* initialize to NULL for sanity check */
180        params[i] = NULL;
181      }
182      JSScope * scope = OBJ_SCOPE(object);
183      for (JSScopeProperty * scope_property = SCOPE_LAST_PROP(scope); scope_property != NULL; scope_property = scope_property->parent) {
184        if (scope_property->getter != js_GetArgument) {
185          continue;
186        }
187        assert(scope_property->flags & SPROP_HAS_SHORTID);
188        assert((uint16) scope_property->shortid < function->nargs);
189        assert(JSID_IS_ATOM(scope_property->id));
190        params[(uint16) scope_property->shortid] = JSID_TO_ATOM(scope_property->id);
191      }
192      for (int i = 0; i < function->nargs; i++) {
193        assert(params[i] != NULL);
194        if (i > 0) {
195          Stream_write_string(f, ", ");
196        }
197        if (ATOM_IS_STRING(params[i])) {
198          print_string_atom(params[i], f);
199        }
200      }
201      Stream_write_string(f, ") {\n");
202      free(params);
203    
204      /* function body */
205      instrument_statement(node->pn_body, f, indent + 2);
206    
207      Stream_write_string(f, "}\n");
208  }  }
209    
210  static void instrument_function_call(JSParseNode * node, FILE * f) {  static void instrument_function_call(JSParseNode * node, Stream * f) {
211    instrument_expression(node->pn_head, f);    instrument_expression(node->pn_head, f);
212    fputc('(', f);    Stream_write_char(f, '(');
213    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) {
214      if (p != node->pn_head->pn_next) {      if (p != node->pn_head->pn_next) {
215        fprintf(f, ", ");        Stream_write_string(f, ", ");
216      }      }
217      instrument_expression(p, f);      instrument_expression(p, f);
218    }    }
219    fputc(')', f);    Stream_write_char(f, ')');
220  }  }
221    
222  /*  /*
# Line 222  Line 232 
232  TOK_INSTANCEOF  binary  TOK_INSTANCEOF  binary
233  TOK_IN          binary  TOK_IN          binary
234  */  */
235  static void instrument_expression(JSParseNode * node, FILE * f) {  static void instrument_expression(JSParseNode * node, Stream * f) {
236    switch (node->pn_type) {    switch (node->pn_type) {
237    case TOK_FUNCTION:    case TOK_FUNCTION:
238      instrument_function(node, f, 0);      instrument_function(node, f, 0, FUNCTION_NORMAL);
239      break;      break;
240    case TOK_COMMA:    case TOK_COMMA:
241      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) {
242        if (p != node->pn_head) {        if (p != node->pn_head) {
243          fprintf(f, ", ");          Stream_write_string(f, ", ");
244        }        }
245        instrument_expression(p, f);        instrument_expression(p, f);
246      }      }
247      break;      break;
248    case TOK_ASSIGN:    case TOK_ASSIGN:
249      instrument_expression(node->pn_left, f);      instrument_expression(node->pn_left, f);
250      fputc(' ', f);      Stream_write_char(f, ' ');
251      switch (node->pn_op) {      switch (node->pn_op) {
252      case JSOP_ADD:      case JSOP_ADD:
253      case JSOP_SUB:      case JSOP_SUB:
# Line 250  Line 260 
260      case JSOP_BITOR:      case JSOP_BITOR:
261      case JSOP_BITXOR:      case JSOP_BITXOR:
262      case JSOP_DIV:      case JSOP_DIV:
263        fprintf(f, "%s", get_op(node->pn_op));        Stream_printf(f, "%s", get_op(node->pn_op));
264        break;        break;
265      default:      default:
266        /* do nothing - it must be a simple assignment */        /* do nothing - it must be a simple assignment */
267        break;        break;
268      }      }
269      fprintf(f, "= ");      Stream_write_string(f, "= ");
270      instrument_expression(node->pn_right, f);      instrument_expression(node->pn_right, f);
271      break;      break;
272    case TOK_HOOK:    case TOK_HOOK:
273      instrument_expression(node->pn_kid1, f);      instrument_expression(node->pn_kid1, f);
274      fprintf(f, "? ");      Stream_write_string(f, "? ");
275      instrument_expression(node->pn_kid2, f);      instrument_expression(node->pn_kid2, f);
276      fprintf(f, ": ");      Stream_write_string(f, ": ");
277      instrument_expression(node->pn_kid3, f);      instrument_expression(node->pn_kid3, f);
278      break;      break;
279    case TOK_OR:    case TOK_OR:
280      instrument_expression(node->pn_left, f);      instrument_expression(node->pn_left, f);
281      fprintf(f, " || ");      Stream_write_string(f, " || ");
282      instrument_expression(node->pn_right, f);      instrument_expression(node->pn_right, f);
283      break;      break;
284    case TOK_AND:    case TOK_AND:
285      instrument_expression(node->pn_left, f);      instrument_expression(node->pn_left, f);
286      fprintf(f, " && ");      Stream_write_string(f, " && ");
287      instrument_expression(node->pn_right, f);      instrument_expression(node->pn_right, f);
288      break;      break;
289    case TOK_BITOR:    case TOK_BITOR:
# Line 289  Line 299 
299      switch (node->pn_arity) {      switch (node->pn_arity) {
300      case PN_BINARY:      case PN_BINARY:
301        instrument_expression(node->pn_left, f);        instrument_expression(node->pn_left, f);
302        fprintf(f, " %s ", get_op(node->pn_op));        Stream_printf(f, " %s ", get_op(node->pn_op));
303        instrument_expression(node->pn_right, f);        instrument_expression(node->pn_right, f);
304        break;        break;
305      case PN_LIST:      case PN_LIST:
306        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) {
307          if (p != node->pn_head) {          if (p != node->pn_head) {
308            fprintf(f, " %s ", get_op(node->pn_op));            Stream_printf(f, " %s ", get_op(node->pn_op));
309          }          }
310          instrument_expression(p, f);          instrument_expression(p, f);
311        }        }
# Line 307  Line 317 
317    case TOK_UNARYOP:    case TOK_UNARYOP:
318      switch (node->pn_op) {      switch (node->pn_op) {
319      case JSOP_NEG:      case JSOP_NEG:
320        fputc('-', f);        Stream_write_char(f, '-');
321        instrument_expression(node->pn_kid, f);        instrument_expression(node->pn_kid, f);
322        break;        break;
323      case JSOP_POS:      case JSOP_POS:
324        fputc('+', f);        Stream_write_char(f, '+');
325        instrument_expression(node->pn_kid, f);        instrument_expression(node->pn_kid, f);
326        break;        break;
327      case JSOP_NOT:      case JSOP_NOT:
328        fputc('!', f);        Stream_write_char(f, '!');
329        instrument_expression(node->pn_kid, f);        instrument_expression(node->pn_kid, f);
330        break;        break;
331      case JSOP_BITNOT:      case JSOP_BITNOT:
332        fputc('~', f);        Stream_write_char(f, '~');
333        instrument_expression(node->pn_kid, f);        instrument_expression(node->pn_kid, f);
334        break;        break;
335      case JSOP_TYPEOF:      case JSOP_TYPEOF:
336        fprintf(f, "typeof ");        Stream_write_string(f, "typeof ");
337        instrument_expression(node->pn_kid, f);        instrument_expression(node->pn_kid, f);
338        break;        break;
339      case JSOP_VOID:      case JSOP_VOID:
340        fprintf(f, "void ");        Stream_write_string(f, "void ");
341        instrument_expression(node->pn_kid, f);        instrument_expression(node->pn_kid, f);
342        break;        break;
343      default:      default:
# Line 344  Line 354 
354      case JSOP_INCNAME:      case JSOP_INCNAME:
355      case JSOP_INCPROP:      case JSOP_INCPROP:
356      case JSOP_INCELEM:      case JSOP_INCELEM:
357        fprintf(f, "++");        Stream_write_string(f, "++");
358        instrument_expression(node->pn_kid, f);        instrument_expression(node->pn_kid, f);
359        break;        break;
360      case JSOP_DECNAME:      case JSOP_DECNAME:
361      case JSOP_DECPROP:      case JSOP_DECPROP:
362      case JSOP_DECELEM:      case JSOP_DECELEM:
363        fprintf(f, "--");        Stream_write_string(f, "--");
364        instrument_expression(node->pn_kid, f);        instrument_expression(node->pn_kid, f);
365        break;        break;
366      case JSOP_NAMEINC:      case JSOP_NAMEINC:
367      case JSOP_PROPINC:      case JSOP_PROPINC:
368      case JSOP_ELEMINC:      case JSOP_ELEMINC:
369        instrument_expression(node->pn_kid, f);        instrument_expression(node->pn_kid, f);
370        fprintf(f, "++");        Stream_write_string(f, "++");
371        break;        break;
372      case JSOP_NAMEDEC:      case JSOP_NAMEDEC:
373      case JSOP_PROPDEC:      case JSOP_PROPDEC:
374      case JSOP_ELEMDEC:      case JSOP_ELEMDEC:
375        instrument_expression(node->pn_kid, f);        instrument_expression(node->pn_kid, f);
376        fprintf(f, "--");        Stream_write_string(f, "--");
377        break;        break;
378      default:      default:
379        abort();        abort();
# Line 371  Line 381 
381      }      }
382      break;      break;
383    case TOK_NEW:    case TOK_NEW:
384      fprintf(f, "new ");      Stream_write_string(f, "new ");
385      instrument_function_call(node, f);      instrument_function_call(node, f);
386      break;      break;
387    case TOK_DELETE:    case TOK_DELETE:
388      fprintf(f, "delete ");      Stream_write_string(f, "delete ");
389      instrument_expression(node->pn_kid, f);      instrument_expression(node->pn_kid, f);
390      break;      break;
391    case TOK_DOT:    case TOK_DOT:
# Line 390  Line 400 
400        JSString * s = ATOM_TO_STRING(node->pn_atom);        JSString * s = ATOM_TO_STRING(node->pn_atom);
401        /* XXX - semantics changed in 1.7 */        /* XXX - semantics changed in 1.7 */
402        if (! ATOM_KEYWORD(node->pn_atom) && js_IsIdentifier(s)) {        if (! ATOM_KEYWORD(node->pn_atom) && js_IsIdentifier(s)) {
403          fputc('.', f);          Stream_write_char(f, '.');
404          print_string_atom(node->pn_atom, f);          print_string_atom(node->pn_atom, f);
405        }        }
406        else {        else {
407          fputc('[', f);          Stream_write_char(f, '[');
408          print_quoted_string_atom(node->pn_atom, f);          print_quoted_string_atom(node->pn_atom, f);
409          fputc(']', f);          Stream_write_char(f, ']');
410        }        }
411      }      }
412      break;      break;
413    case TOK_LB:    case TOK_LB:
414      instrument_expression(node->pn_left, f);      instrument_expression(node->pn_left, f);
415      fputc('[', f);      Stream_write_char(f, '[');
416      instrument_expression(node->pn_right, f);      instrument_expression(node->pn_right, f);
417      fputc(']', f);      Stream_write_char(f, ']');
418      break;      break;
419    case TOK_LP:    case TOK_LP:
420      instrument_function_call(node, f);      instrument_function_call(node, f);
421      break;      break;
422    case TOK_RB:    case TOK_RB:
423      fputc('[', f);      Stream_write_char(f, '[');
424      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) {
425        if (p != node->pn_head) {        if (p != node->pn_head) {
426          fprintf(f, ", ");          Stream_write_string(f, ", ");
427        }        }
428        /* TOK_COMMA is a special case: a hole in the array */        /* TOK_COMMA is a special case: a hole in the array */
429        if (p->pn_type != TOK_COMMA) {        if (p->pn_type != TOK_COMMA) {
# Line 421  Line 431 
431        }        }
432      }      }
433      if (node->pn_extra == PNX_ENDCOMMA) {      if (node->pn_extra == PNX_ENDCOMMA) {
434        fputc(',', f);        Stream_write_char(f, ',');
435      }      }
436      fputc(']', f);      Stream_write_char(f, ']');
437      break;      break;
438    case TOK_RC:    case TOK_RC:
439      fputc('{', f);      Stream_write_char(f, '{');
440      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) {
441        assert(p->pn_type == TOK_COLON);        assert(p->pn_type == TOK_COLON);
442        if (p != node->pn_head) {        if (p != node->pn_head) {
443          fprintf(f, ", ");          Stream_write_string(f, ", ");
444          }
445    
446          /* check whether this is a getter or setter */
447          switch (p->pn_op) {
448          case JSOP_GETTER:
449            Stream_write_string(f, "get ");
450            instrument_expression(p->pn_left, f);
451            instrument_function(p->pn_right, f, 0, FUNCTION_GETTER_OR_SETTER);
452            break;
453          case JSOP_SETTER:
454            Stream_write_string(f, "set ");
455            instrument_expression(p->pn_left, f);
456            instrument_function(p->pn_right, f, 0, FUNCTION_GETTER_OR_SETTER);
457            break;
458          default:
459            instrument_expression(p->pn_left, f);
460            Stream_write_string(f, ": ");
461            instrument_expression(p->pn_right, f);
462            break;
463        }        }
       instrument_expression(p->pn_left, f);  
       fprintf(f, ": ");  
       instrument_expression(p->pn_right, f);  
464      }      }
465      fputc('}', f);      Stream_write_char(f, '}');
466      break;      break;
467    case TOK_RP:    case TOK_RP:
468      fputc('(', f);      Stream_write_char(f, '(');
469      instrument_expression(node->pn_kid, f);      instrument_expression(node->pn_kid, f);
470      fputc(')', f);      Stream_write_char(f, ')');
471      break;      break;
472    case TOK_NAME:    case TOK_NAME:
473      print_string_atom(node->pn_atom, f);      print_string_atom(node->pn_atom, f);
# Line 477  Line 503 
503      To keep the output simple, special-case zero.      To keep the output simple, special-case zero.
504      */      */
505      if (node->pn_dval == 0.0) {      if (node->pn_dval == 0.0) {
506        fprintf(f, "0");        Stream_write_string(f, "0");
507      }      }
508      else {      else {
509        fprintf(f, "%.15g", node->pn_dval);        Stream_printf(f, "%.15g", node->pn_dval);
510      }      }
511      break;      break;
512    case TOK_PRIMARY:    case TOK_PRIMARY:
513      switch (node->pn_op) {      switch (node->pn_op) {
514      case JSOP_TRUE:      case JSOP_TRUE:
515        fprintf(f, "true");        Stream_write_string(f, "true");
516        break;        break;
517      case JSOP_FALSE:      case JSOP_FALSE:
518        fprintf(f, "false");        Stream_write_string(f, "false");
519        break;        break;
520      case JSOP_NULL:      case JSOP_NULL:
521        fprintf(f, "null");        Stream_write_string(f, "null");
522        break;        break;
523      case JSOP_THIS:      case JSOP_THIS:
524        fprintf(f, "this");        Stream_write_string(f, "this");
525        break;        break;
526      /* jsscan.h mentions `super' ??? */      /* jsscan.h mentions `super' ??? */
527      default:      default:
# Line 504  Line 530 
530      break;      break;
531    case TOK_INSTANCEOF:    case TOK_INSTANCEOF:
532      instrument_expression(node->pn_left, f);      instrument_expression(node->pn_left, f);
533      fprintf(f, " instanceof ");      Stream_write_string(f, " instanceof ");
534      instrument_expression(node->pn_right, f);      instrument_expression(node->pn_right, f);
535      break;      break;
536    case TOK_IN:    case TOK_IN:
537      instrument_expression(node->pn_left, f);      instrument_expression(node->pn_left, f);
538      fprintf(f, " in ");      Stream_write_string(f, " in ");
539      instrument_expression(node->pn_right, f);      instrument_expression(node->pn_right, f);
540      break;      break;
541    default:    default:
# Line 517  Line 543 
543    }    }
544  }  }
545    
546  static void instrument_var_statement(JSParseNode * node, FILE * f, int indent) {  static void instrument_var_statement(JSParseNode * node, Stream * f, int indent) {
547    assert(node->pn_arity == PN_LIST);    assert(node->pn_arity == PN_LIST);
548    fprintf(f, "%*s", indent, "");    Stream_printf(f, "%*s", indent, "");
549    fprintf(f, "var ");    Stream_write_string(f, "var ");
550    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) {
551      assert(p->pn_type == TOK_NAME);      assert(p->pn_type == TOK_NAME);
552      assert(p->pn_arity == PN_NAME);      assert(p->pn_arity == PN_NAME);
553      if (p != node->pn_head) {      if (p != node->pn_head) {
554        fprintf(f, ", ");        Stream_write_string(f, ", ");
555      }      }
556      print_string_atom(p->pn_atom, f);      print_string_atom(p->pn_atom, f);
557      if (p->pn_expr != NULL) {      if (p->pn_expr != NULL) {
558        fprintf(f, " = ");        Stream_write_string(f, " = ");
559        instrument_expression(p->pn_expr, f);        instrument_expression(p->pn_expr, f);
560      }      }
561    }    }
562  }  }
563    
564  static void output_statement(JSParseNode * node, FILE * f, int indent) {  static void output_statement(JSParseNode * node, Stream * f, int indent) {
565    switch (node->pn_type) {    switch (node->pn_type) {
566    case TOK_FUNCTION:    case TOK_FUNCTION:
567      instrument_function(node, f, indent);      instrument_function(node, f, indent, FUNCTION_NORMAL);
568      break;      break;
569    case TOK_LC:    case TOK_LC:
570      assert(node->pn_arity == PN_LIST);      assert(node->pn_arity == PN_LIST);
571  /*  /*
572      fprintf(f, "{\n");      Stream_write_string(f, "{\n");
573  */  */
574      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) {
575        instrument_statement(p, f, indent);        instrument_statement(p, f, indent);
576      }      }
577  /*  /*
578      fprintf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
579      fprintf(f, "}\n");      Stream_write_string(f, "}\n");
580  */  */
581      break;      break;
582    case TOK_IF:    case TOK_IF:
583      assert(node->pn_arity == PN_TERNARY);      assert(node->pn_arity == PN_TERNARY);
584      fprintf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
585      fprintf(f, "if (");      Stream_write_string(f, "if (");
586      instrument_expression(node->pn_kid1, f);      instrument_expression(node->pn_kid1, f);
587      fprintf(f, ") {\n");      Stream_write_string(f, ") {\n");
588      instrument_statement(node->pn_kid2, f, indent + 2);      instrument_statement(node->pn_kid2, f, indent + 2);
589      fprintf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
590      fprintf(f, "}\n");      Stream_write_string(f, "}\n");
591      if (node->pn_kid3) {      if (node->pn_kid3) {
592        fprintf(f, "%*s", indent, "");        Stream_printf(f, "%*s", indent, "");
593        fprintf(f, "else {\n");        Stream_write_string(f, "else {\n");
594        instrument_statement(node->pn_kid3, f, indent + 2);        instrument_statement(node->pn_kid3, f, indent + 2);
595        fprintf(f, "%*s", indent, "");        Stream_printf(f, "%*s", indent, "");
596        fprintf(f, "}\n");        Stream_write_string(f, "}\n");
597      }      }
598      break;      break;
599    case TOK_SWITCH:    case TOK_SWITCH:
600      assert(node->pn_arity == PN_BINARY);      assert(node->pn_arity == PN_BINARY);
601      fprintf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
602      fprintf(f, "switch (");      Stream_write_string(f, "switch (");
603      instrument_expression(node->pn_left, f);      instrument_expression(node->pn_left, f);
604      fprintf(f, ") {\n");      Stream_write_string(f, ") {\n");
605      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) {
606        fprintf(f, "%*s", indent, "");        Stream_printf(f, "%*s", indent, "");
607        switch (p->pn_type) {        switch (p->pn_type) {
608        case TOK_CASE:        case TOK_CASE:
609          fprintf(f, "case ");          Stream_write_string(f, "case ");
610          instrument_expression(p->pn_left, f);          instrument_expression(p->pn_left, f);
611          fprintf(f, ":\n");          Stream_write_string(f, ":\n");
612          break;          break;
613        case TOK_DEFAULT:        case TOK_DEFAULT:
614          fprintf(f, "default:\n");          Stream_write_string(f, "default:\n");
615          break;          break;
616        default:        default:
617          abort();          abort();
# Line 593  Line 619 
619        }        }
620        instrument_statement(p->pn_right, f, indent + 2);        instrument_statement(p->pn_right, f, indent + 2);
621      }      }
622      fprintf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
623      fprintf(f, "}\n");      Stream_write_string(f, "}\n");
624      break;      break;
625    case TOK_CASE:    case TOK_CASE:
626    case TOK_DEFAULT:    case TOK_DEFAULT:
# Line 602  Line 628 
628      break;      break;
629    case TOK_WHILE:    case TOK_WHILE:
630      assert(node->pn_arity == PN_BINARY);      assert(node->pn_arity == PN_BINARY);
631      fprintf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
632      fprintf(f, "while (");      Stream_write_string(f, "while (");
633      instrument_expression(node->pn_left, f);      instrument_expression(node->pn_left, f);
634      fprintf(f, ") {\n");      Stream_write_string(f, ") {\n");
635      instrument_statement(node->pn_right, f, indent + 2);      instrument_statement(node->pn_right, f, indent + 2);
636      fprintf(f, "}\n");      Stream_write_string(f, "}\n");
637      break;      break;
638    case TOK_DO:    case TOK_DO:
639      assert(node->pn_arity == PN_BINARY);      assert(node->pn_arity == PN_BINARY);
640      fprintf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
641      fprintf(f, "do {\n");      Stream_write_string(f, "do {\n");
642      instrument_statement(node->pn_left, f, indent + 2);      instrument_statement(node->pn_left, f, indent + 2);
643      fprintf(f, "}\n");      Stream_write_string(f, "}\n");
644      fprintf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
645      fprintf(f, "while (");      Stream_write_string(f, "while (");
646      instrument_expression(node->pn_right, f);      instrument_expression(node->pn_right, f);
647      fprintf(f, ");\n");      Stream_write_string(f, ");\n");
648      break;      break;
649    case TOK_FOR:    case TOK_FOR:
650      assert(node->pn_arity == PN_BINARY);      assert(node->pn_arity == PN_BINARY);
651      fprintf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
652      fprintf(f, "for (");      Stream_write_string(f, "for (");
653      switch (node->pn_left->pn_type) {      switch (node->pn_left->pn_type) {
654      case TOK_IN:      case TOK_IN:
655        /* for/in */        /* for/in */
# Line 646  Line 672 
672          break;          break;
673  */  */
674        }        }
675        fprintf(f, " in ");        Stream_write_string(f, " in ");
676        instrument_expression(node->pn_left->pn_right, f);        instrument_expression(node->pn_left->pn_right, f);
677        break;        break;
678      case TOK_RESERVED:      case TOK_RESERVED:
# Line 660  Line 686 
686            instrument_expression(node->pn_left->pn_kid1, f);            instrument_expression(node->pn_left->pn_kid1, f);
687          }          }
688        }        }
689        fprintf(f, ";");        Stream_write_string(f, ";");
690        if (node->pn_left->pn_kid2) {        if (node->pn_left->pn_kid2) {
691          fputc(' ', f);          Stream_write_char(f, ' ');
692          instrument_expression(node->pn_left->pn_kid2, f);          instrument_expression(node->pn_left->pn_kid2, f);
693        }        }
694        fprintf(f, ";");        Stream_write_string(f, ";");
695        if (node->pn_left->pn_kid3) {        if (node->pn_left->pn_kid3) {
696          fputc(' ', f);          Stream_write_char(f, ' ');
697          instrument_expression(node->pn_left->pn_kid3, f);          instrument_expression(node->pn_left->pn_kid3, f);
698        }        }
699        break;        break;
# Line 675  Line 701 
701        abort();        abort();
702        break;        break;
703      }      }
704      fprintf(f, ") {\n");      Stream_write_string(f, ") {\n");
705      instrument_statement(node->pn_right, f, indent + 2);      instrument_statement(node->pn_right, f, indent + 2);
706      fprintf(f, "}\n");      Stream_write_string(f, "}\n");
707      break;      break;
708    case TOK_THROW:    case TOK_THROW:
709      assert(node->pn_arity == PN_UNARY);      assert(node->pn_arity == PN_UNARY);
710      fprintf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
711      fprintf(f, "throw ");      Stream_write_string(f, "throw ");
712      instrument_expression(node->pn_u.unary.kid, f);      instrument_expression(node->pn_u.unary.kid, f);
713      fprintf(f, ";\n");      Stream_write_string(f, ";\n");
714      break;      break;
715    case TOK_TRY:    case TOK_TRY:
716      fprintf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
717      fprintf(f, "try {\n");      Stream_write_string(f, "try {\n");
718      instrument_statement(node->pn_kid1, f, indent + 2);      instrument_statement(node->pn_kid1, f, indent + 2);
719      fprintf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
720      fprintf(f, "}\n");      Stream_write_string(f, "}\n");
721      {      {
722        for (JSParseNode * catch = node->pn_kid2; catch != NULL; catch = catch->pn_kid2) {        for (JSParseNode * catch = node->pn_kid2; catch != NULL; catch = catch->pn_kid2) {
723          assert(catch->pn_type == TOK_CATCH);          assert(catch->pn_type == TOK_CATCH);
724          fprintf(f, "%*s", indent, "");          Stream_printf(f, "%*s", indent, "");
725          fprintf(f, "catch (");          Stream_write_string(f, "catch (");
726          assert(catch->pn_kid1->pn_arity == PN_NAME);          assert(catch->pn_kid1->pn_arity == PN_NAME);
727          print_string_atom(catch->pn_kid1->pn_atom, f);          print_string_atom(catch->pn_kid1->pn_atom, f);
728          if (catch->pn_kid1->pn_expr) {          if (catch->pn_kid1->pn_expr) {
729            fprintf(f, " if ");            Stream_write_string(f, " if ");
730            instrument_expression(catch->pn_kid1->pn_expr, f);            instrument_expression(catch->pn_kid1->pn_expr, f);
731          }          }
732          fprintf(f, ") {\n");          Stream_write_string(f, ") {\n");
733          instrument_statement(catch->pn_kid3, f, indent + 2);          instrument_statement(catch->pn_kid3, f, indent + 2);
734          fprintf(f, "%*s", indent, "");          Stream_printf(f, "%*s", indent, "");
735          fprintf(f, "}\n");          Stream_write_string(f, "}\n");
736        }        }
737      }      }
738      if (node->pn_kid3) {      if (node->pn_kid3) {
739        fprintf(f, "%*s", indent, "");        Stream_printf(f, "%*s", indent, "");
740        fprintf(f, "finally {\n");        Stream_write_string(f, "finally {\n");
741        instrument_statement(node->pn_kid3, f, indent + 2);        instrument_statement(node->pn_kid3, f, indent + 2);
742        fprintf(f, "%*s", indent, "");        Stream_printf(f, "%*s", indent, "");
743        fprintf(f, "}\n");        Stream_write_string(f, "}\n");
744      }      }
745      break;      break;
746    case TOK_CATCH:    case TOK_CATCH:
# Line 723  Line 749 
749    case TOK_BREAK:    case TOK_BREAK:
750    case TOK_CONTINUE:    case TOK_CONTINUE:
751      assert(node->pn_arity == PN_NAME || node->pn_arity == PN_NULLARY);      assert(node->pn_arity == PN_NAME || node->pn_arity == PN_NULLARY);
752      fprintf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
753      fputs(node->pn_type == TOK_BREAK? "break": "continue", f);      Stream_write_string(f, node->pn_type == TOK_BREAK? "break": "continue");
754      JSAtom * atom = node->pn_u.name.atom;      JSAtom * atom = node->pn_u.name.atom;
755      if (atom != NULL) {      if (atom != NULL) {
756        fputc(' ', f);        Stream_write_char(f, ' ');
757        print_string_atom(node->pn_atom, f);        print_string_atom(node->pn_atom, f);
758      }      }
759      fprintf(f, ";\n");      Stream_write_string(f, ";\n");
760      break;      break;
761    case TOK_WITH:    case TOK_WITH:
762      assert(node->pn_arity == PN_BINARY);      assert(node->pn_arity == PN_BINARY);
763      fprintf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
764      fprintf(f, "with (");      Stream_write_string(f, "with (");
765      instrument_expression(node->pn_left, f);      instrument_expression(node->pn_left, f);
766      fprintf(f, ") {\n");      Stream_write_string(f, ") {\n");
767      instrument_statement(node->pn_right, f, indent + 2);      instrument_statement(node->pn_right, f, indent + 2);
768      fprintf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
769      fprintf(f, "}\n");      Stream_write_string(f, "}\n");
770      break;      break;
771    case TOK_VAR:    case TOK_VAR:
772      instrument_var_statement(node, f, indent);      instrument_var_statement(node, f, indent);
773      fprintf(f, ";\n");      Stream_write_string(f, ";\n");
774      break;      break;
775    case TOK_RETURN:    case TOK_RETURN:
776      assert(node->pn_arity == PN_UNARY);      assert(node->pn_arity == PN_UNARY);
777      fprintf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
778      fprintf(f, "return");      Stream_write_string(f, "return");
779      if (node->pn_kid != NULL) {      if (node->pn_kid != NULL) {
780        fprintf(f, " ");        Stream_write_char(f, ' ');
781        instrument_expression(node->pn_kid, f);        instrument_expression(node->pn_kid, f);
782      }      }
783      fprintf(f, ";\n");      Stream_write_string(f, ";\n");
784      break;      break;
785    case TOK_SEMI:    case TOK_SEMI:
786      assert(node->pn_arity == PN_UNARY);      assert(node->pn_arity == PN_UNARY);
787      fprintf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
788      if (node->pn_kid != NULL) {      if (node->pn_kid != NULL) {
789        instrument_expression(node->pn_kid, f);        instrument_expression(node->pn_kid, f);
790      }      }
791      fprintf(f, ";\n");      Stream_write_string(f, ";\n");
792      break;      break;
793    case TOK_COLON:    case TOK_COLON:
794      assert(node->pn_arity == PN_NAME);      assert(node->pn_arity == PN_NAME);
# Line 770  Line 796 
796      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
797      statement it's supposed to label ...      statement it's supposed to label ...
798      */      */
799      fprintf(f, "%*s", indent < 2? 0: indent - 2, "");      Stream_printf(f, "%*s", indent < 2? 0: indent - 2, "");
800      print_string_atom(node->pn_atom, f);      print_string_atom(node->pn_atom, f);
801      fprintf(f, ":\n");      Stream_write_string(f, ":\n");
802      /*      /*
803      ... use output_statement instead of instrument_statement.      ... use output_statement instead of instrument_statement.
804      */      */
# Line 788  Line 814 
814  TOK_FUNCTION is handled as a statement and as an expression.  TOK_FUNCTION is handled as a statement and as an expression.
815  TOK_EXPORT, TOK_IMPORT are not handled.  TOK_EXPORT, TOK_IMPORT are not handled.
816  */  */
817  static void instrument_statement(JSParseNode * node, FILE * f, int indent) {  static void instrument_statement(JSParseNode * node, Stream * f, int indent) {
818    if (node->pn_type != TOK_LC) {    if (node->pn_type != TOK_LC) {
819      int line = node->pn_pos.begin.lineno;      int line = node->pn_pos.begin.lineno;
820      /* the root node has line number 0 */      /* the root node has line number 0 */
821      if (line != 0) {      if (line != 0) {
822        fprintf(f, "%*s", indent, "");        Stream_printf(f, "%*s", indent, "");
823        fprintf(f, "_$jscoverage['%s'][%d]++;\n", file_id, line);        Stream_printf(f, "_$jscoverage['%s'][%d]++;\n", file_id, line);
824        lines[line - 1] = 1;        lines[line - 1] = 1;
825      }      }
826    }    }
827    output_statement(node, f, indent);    output_statement(node, f, indent);
828  }  }
829    
830  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, Stream * input, Stream * output) {
831    file_id = id;    file_id = id;
832    
833    /* scan the javascript */    /* scan the javascript */
834    JSTokenStream * token_stream = js_NewFileTokenStream(context, NULL, input);    size_t input_length = input->length;
835      jschar * base = js_InflateString(context, (char *) input->data, &input_length);
836      if (base == NULL) {
837        fatal("out of memory");
838      }
839      JSTokenStream * token_stream = js_NewTokenStream(context, base, input_length, NULL, 1, NULL);
840    if (token_stream == NULL) {    if (token_stream == NULL) {
841      fatal("cannot create token stream from file: %s", file_id);      fatal("cannot create token stream from file: %s", file_id);
842    }    }
# Line 822  Line 853 
853    }    }
854    
855    /*    /*
856    Create a temporary file - we can't write directly to the output because we    An instrumented JavaScript file has 3 sections:
857    need to know the line number info first.    1. initialization
858      2. instrumented source code
859      3. original source code
860    */    */
   FILE * temporary = fopen(temporary_file_name, "w+");  
   if (temporary == NULL) {  
     fatal("cannot create temporary file for script: %s", file_id);  
   }  
861    
862    /* write instrumented javascript to the temporary */    Stream * instrumented = Stream_new(0);
863    instrument_statement(node, temporary, 0);    instrument_statement(node, instrumented, 0);
864    
865    /* write line number info to the output */    /* write line number info to the output */
866    fprintf(output, "/* automatically generated by JSCoverage - do not edit */\n");    Stream_write_string(output, "/* automatically generated by JSCoverage - do not edit */\n");
867    fprintf(output, "if (! top._$jscoverage) {\n  top._$jscoverage = {};\n}\n");    Stream_write_string(output, "if (! top._$jscoverage) {\n  top._$jscoverage = {};\n}\n");
868    fprintf(output, "var _$jscoverage = top._$jscoverage;\n");    Stream_write_string(output, "var _$jscoverage = top._$jscoverage;\n");
869    fprintf(output, "if (! _$jscoverage['%s']) {\n", file_id);    Stream_printf(output, "if (! _$jscoverage['%s']) {\n", file_id);
870    fprintf(output, "  _$jscoverage['%s'] = [];\n", file_id);    Stream_printf(output, "  _$jscoverage['%s'] = [];\n", file_id);
871    for (int i = 0; i < num_lines; i++) {    for (int i = 0; i < num_lines; i++) {
872      if (lines[i]) {      if (lines[i]) {
873        fprintf(output, "  _$jscoverage['%s'][%d] = 0;\n", file_id, i + 1);        Stream_printf(output, "  _$jscoverage['%s'][%d] = 0;\n", file_id, i + 1);
874      }      }
875    }    }
876    fprintf(output, "}\n");    Stream_write_string(output, "}\n");
877      free(lines);
878    lines = NULL;    lines = NULL;
879    
880    /* copy the temporary to the output */    /* copy the instrumented source code to the output */
881    fseek(temporary, 0, SEEK_SET);    Stream_write(output, instrumented->data, instrumented->length);
882    copy_stream(temporary, output);    Stream_write_char(output, '\n');
883    
884      /* conditionals */
885      bool has_conditionals = false;
886      size_t line_number = 0;
887      size_t i = 0;
888      while (i < input_length) {
889        line_number++;
890        size_t line_start = i;
891        while (i < input_length && base[i] != '\r' && base[i] != '\n') {
892          i++;
893        }
894        size_t line_end = i;
895        if (i < input_length) {
896          if (base[i] == '\r') {
897            line_end = i;
898            i++;
899            if (i < input_length && base[i] == '\n') {
900              i++;
901            }
902          }
903          else if (base[i] == '\n') {
904            line_end = i;
905            i++;
906          }
907          else {
908            abort();
909          }
910        }
911        char * line = js_DeflateString(context, base + line_start, line_end - line_start);
912        if (str_starts_with(line, "//#JSCOVERAGE_IF")) {
913          if (! has_conditionals) {
914            has_conditionals = true;
915            Stream_printf(output, "_$jscoverage['%s'].conditionals = [];\n", file_id);
916          }
917          Stream_printf(output, "if (!%s) {\n", line + 16);
918          Stream_printf(output, "  _$jscoverage['%s'].conditionals[%d] = ", file_id, line_number);
919        }
920        else if (str_starts_with(line, "//#JSCOVERAGE_ENDIF")) {
921          Stream_printf(output, "%d;\n", line_number);
922          Stream_printf(output, "}\n");
923        }
924        JS_free(context, line);
925      }
926    
927      /* copy the original source to the output */
928      i = 0;
929      while (i < input_length) {
930        Stream_write_string(output, "// ");
931        size_t line_start = i;
932        while (i < input_length && base[i] != '\r' && base[i] != '\n') {
933          i++;
934        }
935    
936        size_t line_end = i;
937        if (i < input_length) {
938          if (base[i] == '\r') {
939            line_end = i;
940            i++;
941            if (i < input_length && base[i] == '\n') {
942              i++;
943            }
944          }
945          else if (base[i] == '\n') {
946            line_end = i;
947            i++;
948          }
949          else {
950            abort();
951          }
952        }
953    
954    fclose(temporary);      char * line = js_DeflateString(context, base + line_start, line_end - line_start);
955        Stream_write_string(output, line);
956        Stream_write_char(output, '\n');
957        JS_free(context, line);
958      }
959    
960      Stream_delete(instrumented);
961    
962      JS_free(context, base);
963    
964    file_id = NULL;    file_id = NULL;
965  }  }
966    
967  void jscoverage_instrument_js(const char * id, FILE * input, FILE * output, const char * temporary_file_name) {  void jscoverage_copy_resources(const char * destination_directory) {
968    instrument_js_stream(id, 0, input, output, temporary_file_name);    copy_resource("jscoverage.html", destination_directory);
969      copy_resource("jscoverage.css", destination_directory);
970      copy_resource("jscoverage.js", destination_directory);
971      copy_resource("jscoverage-throbber.gif", destination_directory);
972      copy_resource("jscoverage-sh_main.js", destination_directory);
973      copy_resource("jscoverage-sh_javascript.js", destination_directory);
974      copy_resource("jscoverage-sh_nedit.css", destination_directory);
975    }
976    
977    /*
978    coverage reports
979    */
980    
981    struct FileCoverageList {
982      FileCoverage * file_coverage;
983      struct FileCoverageList * next;
984    };
985    
986    struct Coverage {
987      JSHashTable * coverage_table;
988      struct FileCoverageList * coverage_list;
989    };
990    
991    static int compare_strings(const void * p1, const void * p2) {
992      return strcmp(p1, p2) == 0;
993    }
994    
995    Coverage * Coverage_new(void) {
996      Coverage * result = xmalloc(sizeof(Coverage));
997      result->coverage_table = JS_NewHashTable(1024, JS_HashString, compare_strings, NULL, NULL, NULL);
998      if (result->coverage_table == NULL) {
999        fatal("cannot create hash table");
1000      }
1001      result->coverage_list = NULL;
1002      return result;
1003    }
1004    
1005    void Coverage_delete(Coverage * coverage) {
1006      JS_HashTableDestroy(coverage->coverage_table);
1007      struct FileCoverageList * p = coverage->coverage_list;
1008      while (p != NULL) {
1009        free(p->file_coverage->lines);
1010        free(p->file_coverage->source);
1011        free(p->file_coverage->id);
1012        free(p->file_coverage);
1013        struct FileCoverageList * q = p;
1014        p = p->next;
1015        free(q);
1016      }
1017      free(coverage);
1018    }
1019    
1020    struct EnumeratorArg {
1021      CoverageForeachFunction f;
1022      void * p;
1023    };
1024    
1025    static intN enumerator(JSHashEntry * entry, intN i, void * arg) {
1026      struct EnumeratorArg * enumerator_arg = arg;
1027      enumerator_arg->f(entry->value, i, enumerator_arg->p);
1028      return 0;
1029    }
1030    
1031    void Coverage_foreach_file(Coverage * coverage, CoverageForeachFunction f, void * p) {
1032      struct EnumeratorArg enumerator_arg;
1033      enumerator_arg.f = f;
1034      enumerator_arg.p = p;
1035      JS_HashTableEnumerateEntries(coverage->coverage_table, enumerator, &enumerator_arg);
1036    }
1037    
1038    int jscoverage_parse_json(Coverage * coverage, const uint8_t * json, size_t length) {
1039      jschar * base = js_InflateString(context, (char *) json, &length);
1040      if (base == NULL) {
1041        fatal("out of memory");
1042      }
1043    
1044      jschar * parenthesized_json = xnew(jschar, addst(length, 2));
1045      parenthesized_json[0] = '(';
1046      memcpy(parenthesized_json + 1, base, mulst(length, sizeof(jschar)));
1047      parenthesized_json[length + 1] = ')';
1048    
1049      JS_free(context, base);
1050    
1051      JSTokenStream * token_stream = js_NewTokenStream(context, parenthesized_json, length + 2, NULL, 1, NULL);
1052      if (token_stream == NULL) {
1053        fatal("cannot create token stream");
1054      }
1055    
1056      JSParseNode * root = js_ParseTokenStream(context, global, token_stream);
1057      free(parenthesized_json);
1058      if (root == NULL) {
1059        return -1;
1060      }
1061    
1062      /* root node must be TOK_LC */
1063      if (root->pn_type != TOK_LC) {
1064        return -1;
1065      }
1066      JSParseNode * semi = root->pn_u.list.head;
1067    
1068      /* the list must be TOK_SEMI and it must contain only one element */
1069      if (semi->pn_type != TOK_SEMI || semi->pn_next != NULL) {
1070        return -1;
1071      }
1072      JSParseNode * parenthesized = semi->pn_kid;
1073    
1074      /* this must be a parenthesized expression */
1075      if (parenthesized->pn_type != TOK_RP) {
1076        return -1;
1077      }
1078      JSParseNode * object = parenthesized->pn_kid;
1079    
1080      /* this must be an object literal */
1081      if (object->pn_type != TOK_RC) {
1082        return -1;
1083      }
1084    
1085      for (JSParseNode * p = object->pn_head; p != NULL; p = p->pn_next) {
1086        /* every element of this list must be TOK_COLON */
1087        if (p->pn_type != TOK_COLON) {
1088          return -1;
1089        }
1090    
1091        /* the key must be a string representing the file */
1092        JSParseNode * key = p->pn_left;
1093        if (key->pn_type != TOK_STRING || ! ATOM_IS_STRING(key->pn_atom)) {
1094          return -1;
1095        }
1096        char * id_bytes = JS_GetStringBytes(ATOM_TO_STRING(key->pn_atom));
1097    
1098        /* the value must be an object literal OR an array */
1099        JSParseNode * value = p->pn_right;
1100        if (! (value->pn_type == TOK_RC || value->pn_type == TOK_RB)) {
1101          return -1;
1102        }
1103    
1104        JSParseNode * array = NULL;
1105        JSParseNode * source = NULL;
1106        if (value->pn_type == TOK_RB) {
1107          /* an array */
1108          array = value;
1109        }
1110        else if (value->pn_type == TOK_RC) {
1111          /* an object literal */
1112          if (value->pn_count != 2) {
1113            return -1;
1114          }
1115          for (JSParseNode * element = value->pn_head; element != NULL; element = element->pn_next) {
1116            if (element->pn_type != TOK_COLON) {
1117              return -1;
1118            }
1119            JSParseNode * left = element->pn_left;
1120            if (left->pn_type != TOK_STRING || ! ATOM_IS_STRING(left->pn_atom)) {
1121              return -1;
1122            }
1123            const char * s = JS_GetStringBytes(ATOM_TO_STRING(left->pn_atom));
1124            if (strcmp(s, "coverage") == 0) {
1125              array = element->pn_right;
1126              if (array->pn_type != TOK_RB) {
1127                return -1;
1128              }
1129            }
1130            else if (strcmp(s, "source") == 0) {
1131              source = element->pn_right;
1132              if (source->pn_type != TOK_STRING || ! ATOM_IS_STRING(source->pn_atom)) {
1133                return -1;
1134              }
1135            }
1136            else {
1137              return -1;
1138            }
1139          }
1140        }
1141        else {
1142          return -1;
1143        }
1144    
1145        if (array == NULL) {
1146          return -1;
1147        }
1148    
1149        /* look up the file in the coverage table */
1150        FileCoverage * file_coverage = JS_HashTableLookup(coverage->coverage_table, id_bytes);
1151        if (file_coverage == NULL) {
1152          /* not there: create a new one */
1153          char * id = xstrdup(id_bytes);
1154          file_coverage = xmalloc(sizeof(FileCoverage));
1155          file_coverage->id = id;
1156          file_coverage->num_lines = array->pn_count - 1;
1157          file_coverage->lines = xnew(int, array->pn_count);
1158          if (source == NULL) {
1159            file_coverage->source = NULL;
1160          }
1161          else {
1162            file_coverage->source = xstrdup(JS_GetStringBytes(ATOM_TO_STRING(source->pn_atom)));
1163          }
1164    
1165          /* set coverage for all lines */
1166          uint32 i = 0;
1167          for (JSParseNode * element = array->pn_head; element != NULL; element = element->pn_next, i++) {
1168            if (element->pn_type == TOK_NUMBER) {
1169              file_coverage->lines[i] = (int) element->pn_dval;
1170            }
1171            else if (element->pn_type == TOK_PRIMARY && element->pn_op == JSOP_NULL) {
1172              file_coverage->lines[i] = -1;
1173            }
1174            else {
1175              return -1;
1176            }
1177          }
1178          assert(i == array->pn_count);
1179    
1180          /* add to the hash table */
1181          JS_HashTableAdd(coverage->coverage_table, id, file_coverage);
1182          struct FileCoverageList * coverage_list = xmalloc(sizeof(struct FileCoverageList));
1183          coverage_list->file_coverage = file_coverage;
1184          coverage_list->next = coverage->coverage_list;
1185          coverage->coverage_list = coverage_list;
1186        }
1187        else {
1188          /* sanity check */
1189          assert(strcmp(file_coverage->id, id_bytes) == 0);
1190          if (file_coverage->num_lines != array->pn_count - 1) {
1191            return -2;
1192          }
1193    
1194          /* merge the coverage */
1195          uint32 i = 0;
1196          for (JSParseNode * element = array->pn_head; element != NULL; element = element->pn_next, i++) {
1197            if (element->pn_type == TOK_NUMBER) {
1198              if (file_coverage->lines[i] == -1) {
1199                return -2;
1200              }
1201              file_coverage->lines[i] += (int) element->pn_dval;
1202            }
1203            else if (element->pn_type == TOK_PRIMARY && element->pn_op == JSOP_NULL) {
1204              if (file_coverage->lines[i] != -1) {
1205                return -2;
1206              }
1207            }
1208            else {
1209              return -1;
1210            }
1211          }
1212          assert(i == array->pn_count);
1213    
1214          /* if this JSON file has source, use it */
1215          if (file_coverage->source == NULL && source != NULL) {
1216            file_coverage->source = xstrdup(JS_GetStringBytes(ATOM_TO_STRING(source->pn_atom)));
1217          }
1218        }
1219      }
1220    
1221      return 0;
1222  }  }

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

  ViewVC Help
Powered by ViewVC 1.1.24