/[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 70 by siliconforks, Thu Nov 22 02:52:38 2007 UTC revision 157 by siliconforks, Sat Sep 13 03:56:45 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 "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    enum FunctionType {
152      FUNCTION_NORMAL,
153      FUNCTION_GETTER_OR_SETTER
154    };
155    
156    static void instrument_function(JSParseNode * node, Stream * f, int indent, enum FunctionType type) {
157      assert(node->pn_arity == PN_FUNC);
158      assert(ATOM_IS_OBJECT(node->pn_funAtom));
159      JSObject * object = ATOM_TO_OBJECT(node->pn_funAtom);
160      assert(JS_ObjectIsFunction(context, object));
161      JSFunction * function = (JSFunction *) JS_GetPrivate(context, object);
162      assert(function);
163      assert(object == function->object);
164      Stream_printf(f, "%*s", indent, "");
165      if (type == FUNCTION_NORMAL) {
166        Stream_write_string(f, "function");
167      }
168    
169      /* function name */
170      if (function->atom) {
171        Stream_write_char(f, ' ');
172        print_string_atom(function->atom, f);
173      }
174    
175  static void instrument_function(JSParseNode * node, FILE * f, int indent) {    /* function parameters */
176      assert(node->pn_arity == PN_FUNC);    Stream_write_string(f, "(");
177      assert(ATOM_IS_OBJECT(node->pn_funAtom));    JSAtom ** params = xnew(JSAtom *, function->nargs);
178      JSObject * object = ATOM_TO_OBJECT(node->pn_funAtom);    for (int i = 0; i < function->nargs; i++) {
179      assert(JS_ObjectIsFunction(context, object));      /* initialize to NULL for sanity check */
180      JSFunction * function = (JSFunction *) JS_GetPrivate(context, object);      params[i] = NULL;
181      assert(function);    }
182      assert(object == function->object);    JSScope * scope = OBJ_SCOPE(object);
183      fprintf(f, "%*s", indent, "");    for (JSScopeProperty * scope_property = SCOPE_LAST_PROP(scope); scope_property != NULL; scope_property = scope_property->parent) {
184      fprintf(f, "function");      if (scope_property->getter != js_GetArgument) {
185          continue;
186      /* function name */      }
187      if (function->atom) {      assert(scope_property->flags & SPROP_HAS_SHORTID);
188        fputc(' ', f);      assert((uint16) scope_property->shortid < function->nargs);
189        print_string_atom(function->atom, f);      assert(JSID_IS_ATOM(scope_property->id));
190      }      params[(uint16) scope_property->shortid] = JSID_TO_ATOM(scope_property->id);
191      }
192      /* function parameters */    for (int i = 0; i < function->nargs; i++) {
193      fprintf(f, "(");      assert(params[i] != NULL);
194      JSAtom ** params = xmalloc(function->nargs * sizeof(JSAtom *));      if (i > 0) {
195      for (int i = 0; i < function->nargs; i++) {        Stream_write_string(f, ", ");
196        /* initialize to NULL for sanity check */      }
197        params[i] = NULL;      if (ATOM_IS_STRING(params[i])) {
198      }        print_string_atom(params[i], f);
199      JSScope * scope = OBJ_SCOPE(object);      }
200      for (JSScopeProperty * scope_property = SCOPE_LAST_PROP(scope); scope_property != NULL; scope_property = scope_property->parent) {    }
201        if (scope_property->getter != js_GetArgument) {    Stream_write_string(f, ") {\n");
202          continue;    free(params);
       }  
       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);  
203    
204      /* function body */    /* function body */
205      instrument_statement(node->pn_body, f, indent + 2);    instrument_statement(node->pn_body, f, indent + 2);
206    
207      fprintf(f, "}\n");    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 385  Line 395 
395      the dot syntax.      the dot syntax.
396      */      */
397      instrument_expression(node->pn_expr, f);      instrument_expression(node->pn_expr, f);
398      /*      assert(ATOM_IS_STRING(node->pn_atom));
399      fputc('.', f);      {
400      print_string_atom(node->pn_atom, f);        JSString * s = ATOM_TO_STRING(node->pn_atom);
401      */        /* XXX - semantics changed in 1.7 */
402      fputc('[', f);        if (! ATOM_KEYWORD(node->pn_atom) && js_IsIdentifier(s)) {
403      print_quoted_string_atom(node->pn_atom, f);          Stream_write_char(f, '.');
404      fputc(']', f);          print_string_atom(node->pn_atom, f);
405          }
406          else {
407            Stream_write_char(f, '[');
408            print_quoted_string_atom(node->pn_atom, f);
409            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 414  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 470  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 497  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 510  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 586  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 595  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 639  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 653  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 668  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 716  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 763  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 781  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 815  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 */    /* conditionals */
881    fseek(temporary, 0, SEEK_SET);    bool has_conditionals = false;
882    copy_stream(temporary, output);    size_t line_number = 0;
883      size_t i = 0;
884      while (i < input_length) {
885        line_number++;
886        size_t line_start = i;
887        while (i < input_length && base[i] != '\r' && base[i] != '\n') {
888          i++;
889        }
890        size_t line_end = i;
891        if (i < input_length) {
892          if (base[i] == '\r') {
893            line_end = i;
894            i++;
895            if (i < input_length && base[i] == '\n') {
896              i++;
897            }
898          }
899          else if (base[i] == '\n') {
900            line_end = i;
901            i++;
902          }
903          else {
904            abort();
905          }
906        }
907        char * line = js_DeflateString(context, base + line_start, line_end - line_start);
908        if (str_starts_with(line, "//#JSCOVERAGE_IF")) {
909          if (! has_conditionals) {
910            has_conditionals = true;
911            Stream_printf(output, "_$jscoverage['%s'].conditionals = [];\n", file_id);
912          }
913          Stream_printf(output, "_$jscoverage['%s'].conditionals[%d] = {condition: function () {return %s;}, ", file_id, line_number, line + 16);
914        }
915        else if (str_starts_with(line, "//#JSCOVERAGE_ENDIF")) {
916          Stream_printf(output, "end: %d};\n", line_number);
917        }
918        JS_free(context, line);
919      }
920    
921      /* copy the instrumented source code to the output */
922      Stream_write(output, instrumented->data, instrumented->length);
923      Stream_write_char(output, '\n');
924    
925      /* copy the original source to the output */
926      i = 0;
927      while (i < input_length) {
928        Stream_write_string(output, "// ");
929        size_t line_start = i;
930        while (i < input_length && base[i] != '\r' && base[i] != '\n') {
931          i++;
932        }
933    
934        size_t line_end = i;
935        if (i < input_length) {
936          if (base[i] == '\r') {
937            line_end = i;
938            i++;
939            if (i < input_length && base[i] == '\n') {
940              i++;
941            }
942          }
943          else if (base[i] == '\n') {
944            line_end = i;
945            i++;
946          }
947          else {
948            abort();
949          }
950        }
951    
952    fclose(temporary);      char * line = js_DeflateString(context, base + line_start, line_end - line_start);
953        Stream_write_string(output, line);
954        Stream_write_char(output, '\n');
955        JS_free(context, line);
956      }
957    
958      Stream_delete(instrumented);
959    
960      JS_free(context, base);
961    
962    file_id = NULL;    file_id = NULL;
963  }  }
964    
965  void jscoverage_instrument_js(const char * id, FILE * input, FILE * output, const char * temporary_file_name) {  void jscoverage_copy_resources(const char * destination_directory) {
966    instrument_js_stream(id, 0, input, output, temporary_file_name);    copy_resource("jscoverage.html", destination_directory);
967      copy_resource("jscoverage.css", destination_directory);
968      copy_resource("jscoverage.js", destination_directory);
969      copy_resource("jscoverage-throbber.gif", destination_directory);
970      copy_resource("jscoverage-sh_main.js", destination_directory);
971      copy_resource("jscoverage-sh_javascript.js", destination_directory);
972      copy_resource("jscoverage-sh_nedit.css", destination_directory);
973    }
974    
975    /*
976    coverage reports
977    */
978    
979    struct FileCoverageList {
980      FileCoverage * file_coverage;
981      struct FileCoverageList * next;
982    };
983    
984    struct Coverage {
985      JSHashTable * coverage_table;
986      struct FileCoverageList * coverage_list;
987    };
988    
989    static int compare_strings(const void * p1, const void * p2) {
990      return strcmp(p1, p2) == 0;
991    }
992    
993    Coverage * Coverage_new(void) {
994      Coverage * result = xmalloc(sizeof(Coverage));
995      result->coverage_table = JS_NewHashTable(1024, JS_HashString, compare_strings, NULL, NULL, NULL);
996      if (result->coverage_table == NULL) {
997        fatal("cannot create hash table");
998      }
999      result->coverage_list = NULL;
1000      return result;
1001    }
1002    
1003    void Coverage_delete(Coverage * coverage) {
1004      JS_HashTableDestroy(coverage->coverage_table);
1005      struct FileCoverageList * p = coverage->coverage_list;
1006      while (p != NULL) {
1007        free(p->file_coverage->lines);
1008        free(p->file_coverage->source);
1009        free(p->file_coverage->id);
1010        free(p->file_coverage);
1011        struct FileCoverageList * q = p;
1012        p = p->next;
1013        free(q);
1014      }
1015      free(coverage);
1016    }
1017    
1018    struct EnumeratorArg {
1019      CoverageForeachFunction f;
1020      void * p;
1021    };
1022    
1023    static intN enumerator(JSHashEntry * entry, intN i, void * arg) {
1024      struct EnumeratorArg * enumerator_arg = arg;
1025      enumerator_arg->f(entry->value, i, enumerator_arg->p);
1026      return 0;
1027    }
1028    
1029    void Coverage_foreach_file(Coverage * coverage, CoverageForeachFunction f, void * p) {
1030      struct EnumeratorArg enumerator_arg;
1031      enumerator_arg.f = f;
1032      enumerator_arg.p = p;
1033      JS_HashTableEnumerateEntries(coverage->coverage_table, enumerator, &enumerator_arg);
1034    }
1035    
1036    int jscoverage_parse_json(Coverage * coverage, const uint8_t * json, size_t length) {
1037      jschar * base = js_InflateString(context, (char *) json, &length);
1038      if (base == NULL) {
1039        fatal("out of memory");
1040      }
1041    
1042      jschar * parenthesized_json = xnew(jschar, addst(length, 2));
1043      parenthesized_json[0] = '(';
1044      memcpy(parenthesized_json + 1, base, mulst(length, sizeof(jschar)));
1045      parenthesized_json[length + 1] = ')';
1046    
1047      JS_free(context, base);
1048    
1049      JSTokenStream * token_stream = js_NewTokenStream(context, parenthesized_json, length + 2, NULL, 1, NULL);
1050      if (token_stream == NULL) {
1051        fatal("cannot create token stream");
1052      }
1053    
1054      JSParseNode * root = js_ParseTokenStream(context, global, token_stream);
1055      free(parenthesized_json);
1056      if (root == NULL) {
1057        return -1;
1058      }
1059    
1060      /* root node must be TOK_LC */
1061      if (root->pn_type != TOK_LC) {
1062        return -1;
1063      }
1064      JSParseNode * semi = root->pn_u.list.head;
1065    
1066      /* the list must be TOK_SEMI and it must contain only one element */
1067      if (semi->pn_type != TOK_SEMI || semi->pn_next != NULL) {
1068        return -1;
1069      }
1070      JSParseNode * parenthesized = semi->pn_kid;
1071    
1072      /* this must be a parenthesized expression */
1073      if (parenthesized->pn_type != TOK_RP) {
1074        return -1;
1075      }
1076      JSParseNode * object = parenthesized->pn_kid;
1077    
1078      /* this must be an object literal */
1079      if (object->pn_type != TOK_RC) {
1080        return -1;
1081      }
1082    
1083      for (JSParseNode * p = object->pn_head; p != NULL; p = p->pn_next) {
1084        /* every element of this list must be TOK_COLON */
1085        if (p->pn_type != TOK_COLON) {
1086          return -1;
1087        }
1088    
1089        /* the key must be a string representing the file */
1090        JSParseNode * key = p->pn_left;
1091        if (key->pn_type != TOK_STRING || ! ATOM_IS_STRING(key->pn_atom)) {
1092          return -1;
1093        }
1094        char * id_bytes = JS_GetStringBytes(ATOM_TO_STRING(key->pn_atom));
1095    
1096        /* the value must be an object literal OR an array */
1097        JSParseNode * value = p->pn_right;
1098        if (! (value->pn_type == TOK_RC || value->pn_type == TOK_RB)) {
1099          return -1;
1100        }
1101    
1102        JSParseNode * array = NULL;
1103        JSParseNode * source = NULL;
1104        if (value->pn_type == TOK_RB) {
1105          /* an array */
1106          array = value;
1107        }
1108        else if (value->pn_type == TOK_RC) {
1109          /* an object literal */
1110          if (value->pn_count != 2) {
1111            return -1;
1112          }
1113          for (JSParseNode * element = value->pn_head; element != NULL; element = element->pn_next) {
1114            if (element->pn_type != TOK_COLON) {
1115              return -1;
1116            }
1117            JSParseNode * left = element->pn_left;
1118            if (left->pn_type != TOK_STRING || ! ATOM_IS_STRING(left->pn_atom)) {
1119              return -1;
1120            }
1121            const char * s = JS_GetStringBytes(ATOM_TO_STRING(left->pn_atom));
1122            if (strcmp(s, "coverage") == 0) {
1123              array = element->pn_right;
1124              if (array->pn_type != TOK_RB) {
1125                return -1;
1126              }
1127            }
1128            else if (strcmp(s, "source") == 0) {
1129              source = element->pn_right;
1130              if (source->pn_type != TOK_STRING || ! ATOM_IS_STRING(source->pn_atom)) {
1131                return -1;
1132              }
1133            }
1134            else {
1135              return -1;
1136            }
1137          }
1138        }
1139        else {
1140          return -1;
1141        }
1142    
1143        if (array == NULL) {
1144          return -1;
1145        }
1146    
1147        /* look up the file in the coverage table */
1148        FileCoverage * file_coverage = JS_HashTableLookup(coverage->coverage_table, id_bytes);
1149        if (file_coverage == NULL) {
1150          /* not there: create a new one */
1151          char * id = xstrdup(id_bytes);
1152          file_coverage = xmalloc(sizeof(FileCoverage));
1153          file_coverage->id = id;
1154          file_coverage->num_lines = array->pn_count - 1;
1155          file_coverage->lines = xnew(int, array->pn_count);
1156          if (source == NULL) {
1157            file_coverage->source = NULL;
1158          }
1159          else {
1160            file_coverage->source = xstrdup(JS_GetStringBytes(ATOM_TO_STRING(source->pn_atom)));
1161          }
1162    
1163          /* set coverage for all lines */
1164          uint32 i = 0;
1165          for (JSParseNode * element = array->pn_head; element != NULL; element = element->pn_next, i++) {
1166            if (element->pn_type == TOK_NUMBER) {
1167              file_coverage->lines[i] = (int) element->pn_dval;
1168            }
1169            else if (element->pn_type == TOK_PRIMARY && element->pn_op == JSOP_NULL) {
1170              file_coverage->lines[i] = -1;
1171            }
1172            else {
1173              return -1;
1174            }
1175          }
1176          assert(i == array->pn_count);
1177    
1178          /* add to the hash table */
1179          JS_HashTableAdd(coverage->coverage_table, id, file_coverage);
1180          struct FileCoverageList * coverage_list = xmalloc(sizeof(struct FileCoverageList));
1181          coverage_list->file_coverage = file_coverage;
1182          coverage_list->next = coverage->coverage_list;
1183          coverage->coverage_list = coverage_list;
1184        }
1185        else {
1186          /* sanity check */
1187          assert(strcmp(file_coverage->id, id_bytes) == 0);
1188          if (file_coverage->num_lines != array->pn_count - 1) {
1189            return -2;
1190          }
1191    
1192          /* merge the coverage */
1193          uint32 i = 0;
1194          for (JSParseNode * element = array->pn_head; element != NULL; element = element->pn_next, i++) {
1195            if (element->pn_type == TOK_NUMBER) {
1196              if (file_coverage->lines[i] == -1) {
1197                return -2;
1198              }
1199              file_coverage->lines[i] += (int) element->pn_dval;
1200            }
1201            else if (element->pn_type == TOK_PRIMARY && element->pn_op == JSOP_NULL) {
1202              if (file_coverage->lines[i] != -1) {
1203                return -2;
1204              }
1205            }
1206            else {
1207              return -1;
1208            }
1209          }
1210          assert(i == array->pn_count);
1211    
1212          /* if this JSON file has source, use it */
1213          if (file_coverage->source == NULL && source != NULL) {
1214            file_coverage->source = xstrdup(JS_GetStringBytes(ATOM_TO_STRING(source->pn_atom)));
1215          }
1216        }
1217      }
1218    
1219      return 0;
1220  }  }

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

  ViewVC Help
Powered by ViewVC 1.1.24