/[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 116 by siliconforks, Sat May 31 21:42:36 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) {  static void instrument_function(JSParseNode * node, Stream * f, int indent) {
152      assert(node->pn_arity == PN_FUNC);      assert(node->pn_arity == PN_FUNC);
153      assert(ATOM_IS_OBJECT(node->pn_funAtom));      assert(ATOM_IS_OBJECT(node->pn_funAtom));
154      JSObject * object = ATOM_TO_OBJECT(node->pn_funAtom);      JSObject * object = ATOM_TO_OBJECT(node->pn_funAtom);
# Line 153  Line 156 
156      JSFunction * function = (JSFunction *) JS_GetPrivate(context, object);      JSFunction * function = (JSFunction *) JS_GetPrivate(context, object);
157      assert(function);      assert(function);
158      assert(object == function->object);      assert(object == function->object);
159      fprintf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
160      fprintf(f, "function");      Stream_write_string(f, "function");
161    
162      /* function name */      /* function name */
163      if (function->atom) {      if (function->atom) {
164        fputc(' ', f);        Stream_write_char(f, ' ');
165        print_string_atom(function->atom, f);        print_string_atom(function->atom, f);
166      }      }
167    
168      /* function parameters */      /* function parameters */
169      fprintf(f, "(");      Stream_write_string(f, "(");
170      JSAtom ** params = xmalloc(function->nargs * sizeof(JSAtom *));      JSAtom ** params = xnew(JSAtom *, function->nargs);
171      for (int i = 0; i < function->nargs; i++) {      for (int i = 0; i < function->nargs; i++) {
172        /* initialize to NULL for sanity check */        /* initialize to NULL for sanity check */
173        params[i] = NULL;        params[i] = NULL;
# Line 182  Line 185 
185      for (int i = 0; i < function->nargs; i++) {      for (int i = 0; i < function->nargs; i++) {
186        assert(params[i] != NULL);        assert(params[i] != NULL);
187        if (i > 0) {        if (i > 0) {
188          fprintf(f, ", ");          Stream_write_string(f, ", ");
189        }        }
190        if (ATOM_IS_STRING(params[i])) {        if (ATOM_IS_STRING(params[i])) {
191          print_string_atom(params[i], f);          print_string_atom(params[i], f);
192        }        }
193      }      }
194      fprintf(f, ") {\n");      Stream_write_string(f, ") {\n");
195      free(params);      free(params);
196    
197      /* function body */      /* function body */
198      instrument_statement(node->pn_body, f, indent + 2);      instrument_statement(node->pn_body, f, indent + 2);
199    
200      fprintf(f, "}\n");      Stream_write_string(f, "}\n");
201  }  }
202    
203  static void instrument_function_call(JSParseNode * node, FILE * f) {  static void instrument_function_call(JSParseNode * node, Stream * f) {
204    instrument_expression(node->pn_head, f);    instrument_expression(node->pn_head, f);
205    fputc('(', f);    Stream_write_char(f, '(');
206    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) {
207      if (p != node->pn_head->pn_next) {      if (p != node->pn_head->pn_next) {
208        fprintf(f, ", ");        Stream_write_string(f, ", ");
209      }      }
210      instrument_expression(p, f);      instrument_expression(p, f);
211    }    }
212    fputc(')', f);    Stream_write_char(f, ')');
213  }  }
214    
215  /*  /*
# Line 222  Line 225 
225  TOK_INSTANCEOF  binary  TOK_INSTANCEOF  binary
226  TOK_IN          binary  TOK_IN          binary
227  */  */
228  static void instrument_expression(JSParseNode * node, FILE * f) {  static void instrument_expression(JSParseNode * node, Stream * f) {
229    switch (node->pn_type) {    switch (node->pn_type) {
230    case TOK_FUNCTION:    case TOK_FUNCTION:
231      instrument_function(node, f, 0);      instrument_function(node, f, 0);
# Line 230  Line 233 
233    case TOK_COMMA:    case TOK_COMMA:
234      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) {
235        if (p != node->pn_head) {        if (p != node->pn_head) {
236          fprintf(f, ", ");          Stream_write_string(f, ", ");
237        }        }
238        instrument_expression(p, f);        instrument_expression(p, f);
239      }      }
240      break;      break;
241    case TOK_ASSIGN:    case TOK_ASSIGN:
242      instrument_expression(node->pn_left, f);      instrument_expression(node->pn_left, f);
243      fputc(' ', f);      Stream_write_char(f, ' ');
244      switch (node->pn_op) {      switch (node->pn_op) {
245      case JSOP_ADD:      case JSOP_ADD:
246      case JSOP_SUB:      case JSOP_SUB:
# Line 250  Line 253 
253      case JSOP_BITOR:      case JSOP_BITOR:
254      case JSOP_BITXOR:      case JSOP_BITXOR:
255      case JSOP_DIV:      case JSOP_DIV:
256        fprintf(f, "%s", get_op(node->pn_op));        Stream_printf(f, "%s", get_op(node->pn_op));
257        break;        break;
258      default:      default:
259        /* do nothing - it must be a simple assignment */        /* do nothing - it must be a simple assignment */
260        break;        break;
261      }      }
262      fprintf(f, "= ");      Stream_write_string(f, "= ");
263      instrument_expression(node->pn_right, f);      instrument_expression(node->pn_right, f);
264      break;      break;
265    case TOK_HOOK:    case TOK_HOOK:
266      instrument_expression(node->pn_kid1, f);      instrument_expression(node->pn_kid1, f);
267      fprintf(f, "? ");      Stream_write_string(f, "? ");
268      instrument_expression(node->pn_kid2, f);      instrument_expression(node->pn_kid2, f);
269      fprintf(f, ": ");      Stream_write_string(f, ": ");
270      instrument_expression(node->pn_kid3, f);      instrument_expression(node->pn_kid3, f);
271      break;      break;
272    case TOK_OR:    case TOK_OR:
273      instrument_expression(node->pn_left, f);      instrument_expression(node->pn_left, f);
274      fprintf(f, " || ");      Stream_write_string(f, " || ");
275      instrument_expression(node->pn_right, f);      instrument_expression(node->pn_right, f);
276      break;      break;
277    case TOK_AND:    case TOK_AND:
278      instrument_expression(node->pn_left, f);      instrument_expression(node->pn_left, f);
279      fprintf(f, " && ");      Stream_write_string(f, " && ");
280      instrument_expression(node->pn_right, f);      instrument_expression(node->pn_right, f);
281      break;      break;
282    case TOK_BITOR:    case TOK_BITOR:
# Line 289  Line 292 
292      switch (node->pn_arity) {      switch (node->pn_arity) {
293      case PN_BINARY:      case PN_BINARY:
294        instrument_expression(node->pn_left, f);        instrument_expression(node->pn_left, f);
295        fprintf(f, " %s ", get_op(node->pn_op));        Stream_printf(f, " %s ", get_op(node->pn_op));
296        instrument_expression(node->pn_right, f);        instrument_expression(node->pn_right, f);
297        break;        break;
298      case PN_LIST:      case PN_LIST:
299        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) {
300          if (p != node->pn_head) {          if (p != node->pn_head) {
301            fprintf(f, " %s ", get_op(node->pn_op));            Stream_printf(f, " %s ", get_op(node->pn_op));
302          }          }
303          instrument_expression(p, f);          instrument_expression(p, f);
304        }        }
# Line 307  Line 310 
310    case TOK_UNARYOP:    case TOK_UNARYOP:
311      switch (node->pn_op) {      switch (node->pn_op) {
312      case JSOP_NEG:      case JSOP_NEG:
313        fputc('-', f);        Stream_write_char(f, '-');
314        instrument_expression(node->pn_kid, f);        instrument_expression(node->pn_kid, f);
315        break;        break;
316      case JSOP_POS:      case JSOP_POS:
317        fputc('+', f);        Stream_write_char(f, '+');
318        instrument_expression(node->pn_kid, f);        instrument_expression(node->pn_kid, f);
319        break;        break;
320      case JSOP_NOT:      case JSOP_NOT:
321        fputc('!', f);        Stream_write_char(f, '!');
322        instrument_expression(node->pn_kid, f);        instrument_expression(node->pn_kid, f);
323        break;        break;
324      case JSOP_BITNOT:      case JSOP_BITNOT:
325        fputc('~', f);        Stream_write_char(f, '~');
326        instrument_expression(node->pn_kid, f);        instrument_expression(node->pn_kid, f);
327        break;        break;
328      case JSOP_TYPEOF:      case JSOP_TYPEOF:
329        fprintf(f, "typeof ");        Stream_write_string(f, "typeof ");
330        instrument_expression(node->pn_kid, f);        instrument_expression(node->pn_kid, f);
331        break;        break;
332      case JSOP_VOID:      case JSOP_VOID:
333        fprintf(f, "void ");        Stream_write_string(f, "void ");
334        instrument_expression(node->pn_kid, f);        instrument_expression(node->pn_kid, f);
335        break;        break;
336      default:      default:
# Line 344  Line 347 
347      case JSOP_INCNAME:      case JSOP_INCNAME:
348      case JSOP_INCPROP:      case JSOP_INCPROP:
349      case JSOP_INCELEM:      case JSOP_INCELEM:
350        fprintf(f, "++");        Stream_write_string(f, "++");
351        instrument_expression(node->pn_kid, f);        instrument_expression(node->pn_kid, f);
352        break;        break;
353      case JSOP_DECNAME:      case JSOP_DECNAME:
354      case JSOP_DECPROP:      case JSOP_DECPROP:
355      case JSOP_DECELEM:      case JSOP_DECELEM:
356        fprintf(f, "--");        Stream_write_string(f, "--");
357        instrument_expression(node->pn_kid, f);        instrument_expression(node->pn_kid, f);
358        break;        break;
359      case JSOP_NAMEINC:      case JSOP_NAMEINC:
360      case JSOP_PROPINC:      case JSOP_PROPINC:
361      case JSOP_ELEMINC:      case JSOP_ELEMINC:
362        instrument_expression(node->pn_kid, f);        instrument_expression(node->pn_kid, f);
363        fprintf(f, "++");        Stream_write_string(f, "++");
364        break;        break;
365      case JSOP_NAMEDEC:      case JSOP_NAMEDEC:
366      case JSOP_PROPDEC:      case JSOP_PROPDEC:
367      case JSOP_ELEMDEC:      case JSOP_ELEMDEC:
368        instrument_expression(node->pn_kid, f);        instrument_expression(node->pn_kid, f);
369        fprintf(f, "--");        Stream_write_string(f, "--");
370        break;        break;
371      default:      default:
372        abort();        abort();
# Line 371  Line 374 
374      }      }
375      break;      break;
376    case TOK_NEW:    case TOK_NEW:
377      fprintf(f, "new ");      Stream_write_string(f, "new ");
378      instrument_function_call(node, f);      instrument_function_call(node, f);
379      break;      break;
380    case TOK_DELETE:    case TOK_DELETE:
381      fprintf(f, "delete ");      Stream_write_string(f, "delete ");
382      instrument_expression(node->pn_kid, f);      instrument_expression(node->pn_kid, f);
383      break;      break;
384    case TOK_DOT:    case TOK_DOT:
# Line 390  Line 393 
393        JSString * s = ATOM_TO_STRING(node->pn_atom);        JSString * s = ATOM_TO_STRING(node->pn_atom);
394        /* XXX - semantics changed in 1.7 */        /* XXX - semantics changed in 1.7 */
395        if (! ATOM_KEYWORD(node->pn_atom) && js_IsIdentifier(s)) {        if (! ATOM_KEYWORD(node->pn_atom) && js_IsIdentifier(s)) {
396          fputc('.', f);          Stream_write_char(f, '.');
397          print_string_atom(node->pn_atom, f);          print_string_atom(node->pn_atom, f);
398        }        }
399        else {        else {
400          fputc('[', f);          Stream_write_char(f, '[');
401          print_quoted_string_atom(node->pn_atom, f);          print_quoted_string_atom(node->pn_atom, f);
402          fputc(']', f);          Stream_write_char(f, ']');
403        }        }
404      }      }
405      break;      break;
406    case TOK_LB:    case TOK_LB:
407      instrument_expression(node->pn_left, f);      instrument_expression(node->pn_left, f);
408      fputc('[', f);      Stream_write_char(f, '[');
409      instrument_expression(node->pn_right, f);      instrument_expression(node->pn_right, f);
410      fputc(']', f);      Stream_write_char(f, ']');
411      break;      break;
412    case TOK_LP:    case TOK_LP:
413      instrument_function_call(node, f);      instrument_function_call(node, f);
414      break;      break;
415    case TOK_RB:    case TOK_RB:
416      fputc('[', f);      Stream_write_char(f, '[');
417      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) {
418        if (p != node->pn_head) {        if (p != node->pn_head) {
419          fprintf(f, ", ");          Stream_write_string(f, ", ");
420        }        }
421        /* TOK_COMMA is a special case: a hole in the array */        /* TOK_COMMA is a special case: a hole in the array */
422        if (p->pn_type != TOK_COMMA) {        if (p->pn_type != TOK_COMMA) {
# Line 421  Line 424 
424        }        }
425      }      }
426      if (node->pn_extra == PNX_ENDCOMMA) {      if (node->pn_extra == PNX_ENDCOMMA) {
427        fputc(',', f);        Stream_write_char(f, ',');
428      }      }
429      fputc(']', f);      Stream_write_char(f, ']');
430      break;      break;
431    case TOK_RC:    case TOK_RC:
432      fputc('{', f);      Stream_write_char(f, '{');
433      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) {
434        assert(p->pn_type == TOK_COLON);        assert(p->pn_type == TOK_COLON);
435        if (p != node->pn_head) {        if (p != node->pn_head) {
436          fprintf(f, ", ");          Stream_write_string(f, ", ");
437        }        }
438        instrument_expression(p->pn_left, f);        instrument_expression(p->pn_left, f);
439        fprintf(f, ": ");        Stream_write_string(f, ": ");
440        instrument_expression(p->pn_right, f);        instrument_expression(p->pn_right, f);
441      }      }
442      fputc('}', f);      Stream_write_char(f, '}');
443      break;      break;
444    case TOK_RP:    case TOK_RP:
445      fputc('(', f);      Stream_write_char(f, '(');
446      instrument_expression(node->pn_kid, f);      instrument_expression(node->pn_kid, f);
447      fputc(')', f);      Stream_write_char(f, ')');
448      break;      break;
449    case TOK_NAME:    case TOK_NAME:
450      print_string_atom(node->pn_atom, f);      print_string_atom(node->pn_atom, f);
# Line 477  Line 480 
480      To keep the output simple, special-case zero.      To keep the output simple, special-case zero.
481      */      */
482      if (node->pn_dval == 0.0) {      if (node->pn_dval == 0.0) {
483        fprintf(f, "0");        Stream_write_string(f, "0");
484      }      }
485      else {      else {
486        fprintf(f, "%.15g", node->pn_dval);        Stream_printf(f, "%.15g", node->pn_dval);
487      }      }
488      break;      break;
489    case TOK_PRIMARY:    case TOK_PRIMARY:
490      switch (node->pn_op) {      switch (node->pn_op) {
491      case JSOP_TRUE:      case JSOP_TRUE:
492        fprintf(f, "true");        Stream_write_string(f, "true");
493        break;        break;
494      case JSOP_FALSE:      case JSOP_FALSE:
495        fprintf(f, "false");        Stream_write_string(f, "false");
496        break;        break;
497      case JSOP_NULL:      case JSOP_NULL:
498        fprintf(f, "null");        Stream_write_string(f, "null");
499        break;        break;
500      case JSOP_THIS:      case JSOP_THIS:
501        fprintf(f, "this");        Stream_write_string(f, "this");
502        break;        break;
503      /* jsscan.h mentions `super' ??? */      /* jsscan.h mentions `super' ??? */
504      default:      default:
# Line 504  Line 507 
507      break;      break;
508    case TOK_INSTANCEOF:    case TOK_INSTANCEOF:
509      instrument_expression(node->pn_left, f);      instrument_expression(node->pn_left, f);
510      fprintf(f, " instanceof ");      Stream_write_string(f, " instanceof ");
511      instrument_expression(node->pn_right, f);      instrument_expression(node->pn_right, f);
512      break;      break;
513    case TOK_IN:    case TOK_IN:
514      instrument_expression(node->pn_left, f);      instrument_expression(node->pn_left, f);
515      fprintf(f, " in ");      Stream_write_string(f, " in ");
516      instrument_expression(node->pn_right, f);      instrument_expression(node->pn_right, f);
517      break;      break;
518    default:    default:
# Line 517  Line 520 
520    }    }
521  }  }
522    
523  static void instrument_var_statement(JSParseNode * node, FILE * f, int indent) {  static void instrument_var_statement(JSParseNode * node, Stream * f, int indent) {
524    assert(node->pn_arity == PN_LIST);    assert(node->pn_arity == PN_LIST);
525    fprintf(f, "%*s", indent, "");    Stream_printf(f, "%*s", indent, "");
526    fprintf(f, "var ");    Stream_write_string(f, "var ");
527    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) {
528      assert(p->pn_type == TOK_NAME);      assert(p->pn_type == TOK_NAME);
529      assert(p->pn_arity == PN_NAME);      assert(p->pn_arity == PN_NAME);
530      if (p != node->pn_head) {      if (p != node->pn_head) {
531        fprintf(f, ", ");        Stream_write_string(f, ", ");
532      }      }
533      print_string_atom(p->pn_atom, f);      print_string_atom(p->pn_atom, f);
534      if (p->pn_expr != NULL) {      if (p->pn_expr != NULL) {
535        fprintf(f, " = ");        Stream_write_string(f, " = ");
536        instrument_expression(p->pn_expr, f);        instrument_expression(p->pn_expr, f);
537      }      }
538    }    }
539  }  }
540    
541  static void output_statement(JSParseNode * node, FILE * f, int indent) {  static void output_statement(JSParseNode * node, Stream * f, int indent) {
542    switch (node->pn_type) {    switch (node->pn_type) {
543    case TOK_FUNCTION:    case TOK_FUNCTION:
544      instrument_function(node, f, indent);      instrument_function(node, f, indent);
# Line 543  Line 546 
546    case TOK_LC:    case TOK_LC:
547      assert(node->pn_arity == PN_LIST);      assert(node->pn_arity == PN_LIST);
548  /*  /*
549      fprintf(f, "{\n");      Stream_write_string(f, "{\n");
550  */  */
551      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) {
552        instrument_statement(p, f, indent);        instrument_statement(p, f, indent);
553      }      }
554  /*  /*
555      fprintf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
556      fprintf(f, "}\n");      Stream_write_string(f, "}\n");
557  */  */
558      break;      break;
559    case TOK_IF:    case TOK_IF:
560      assert(node->pn_arity == PN_TERNARY);      assert(node->pn_arity == PN_TERNARY);
561      fprintf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
562      fprintf(f, "if (");      Stream_write_string(f, "if (");
563      instrument_expression(node->pn_kid1, f);      instrument_expression(node->pn_kid1, f);
564      fprintf(f, ") {\n");      Stream_write_string(f, ") {\n");
565      instrument_statement(node->pn_kid2, f, indent + 2);      instrument_statement(node->pn_kid2, f, indent + 2);
566      fprintf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
567      fprintf(f, "}\n");      Stream_write_string(f, "}\n");
568      if (node->pn_kid3) {      if (node->pn_kid3) {
569        fprintf(f, "%*s", indent, "");        Stream_printf(f, "%*s", indent, "");
570        fprintf(f, "else {\n");        Stream_write_string(f, "else {\n");
571        instrument_statement(node->pn_kid3, f, indent + 2);        instrument_statement(node->pn_kid3, f, indent + 2);
572        fprintf(f, "%*s", indent, "");        Stream_printf(f, "%*s", indent, "");
573        fprintf(f, "}\n");        Stream_write_string(f, "}\n");
574      }      }
575      break;      break;
576    case TOK_SWITCH:    case TOK_SWITCH:
577      assert(node->pn_arity == PN_BINARY);      assert(node->pn_arity == PN_BINARY);
578      fprintf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
579      fprintf(f, "switch (");      Stream_write_string(f, "switch (");
580      instrument_expression(node->pn_left, f);      instrument_expression(node->pn_left, f);
581      fprintf(f, ") {\n");      Stream_write_string(f, ") {\n");
582      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) {
583        fprintf(f, "%*s", indent, "");        Stream_printf(f, "%*s", indent, "");
584        switch (p->pn_type) {        switch (p->pn_type) {
585        case TOK_CASE:        case TOK_CASE:
586          fprintf(f, "case ");          Stream_write_string(f, "case ");
587          instrument_expression(p->pn_left, f);          instrument_expression(p->pn_left, f);
588          fprintf(f, ":\n");          Stream_write_string(f, ":\n");
589          break;          break;
590        case TOK_DEFAULT:        case TOK_DEFAULT:
591          fprintf(f, "default:\n");          Stream_write_string(f, "default:\n");
592          break;          break;
593        default:        default:
594          abort();          abort();
# Line 593  Line 596 
596        }        }
597        instrument_statement(p->pn_right, f, indent + 2);        instrument_statement(p->pn_right, f, indent + 2);
598      }      }
599      fprintf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
600      fprintf(f, "}\n");      Stream_write_string(f, "}\n");
601      break;      break;
602    case TOK_CASE:    case TOK_CASE:
603    case TOK_DEFAULT:    case TOK_DEFAULT:
# Line 602  Line 605 
605      break;      break;
606    case TOK_WHILE:    case TOK_WHILE:
607      assert(node->pn_arity == PN_BINARY);      assert(node->pn_arity == PN_BINARY);
608      fprintf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
609      fprintf(f, "while (");      Stream_write_string(f, "while (");
610      instrument_expression(node->pn_left, f);      instrument_expression(node->pn_left, f);
611      fprintf(f, ") {\n");      Stream_write_string(f, ") {\n");
612      instrument_statement(node->pn_right, f, indent + 2);      instrument_statement(node->pn_right, f, indent + 2);
613      fprintf(f, "}\n");      Stream_write_string(f, "}\n");
614      break;      break;
615    case TOK_DO:    case TOK_DO:
616      assert(node->pn_arity == PN_BINARY);      assert(node->pn_arity == PN_BINARY);
617      fprintf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
618      fprintf(f, "do {\n");      Stream_write_string(f, "do {\n");
619      instrument_statement(node->pn_left, f, indent + 2);      instrument_statement(node->pn_left, f, indent + 2);
620      fprintf(f, "}\n");      Stream_write_string(f, "}\n");
621      fprintf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
622      fprintf(f, "while (");      Stream_write_string(f, "while (");
623      instrument_expression(node->pn_right, f);      instrument_expression(node->pn_right, f);
624      fprintf(f, ");\n");      Stream_write_string(f, ");\n");
625      break;      break;
626    case TOK_FOR:    case TOK_FOR:
627      assert(node->pn_arity == PN_BINARY);      assert(node->pn_arity == PN_BINARY);
628      fprintf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
629      fprintf(f, "for (");      Stream_write_string(f, "for (");
630      switch (node->pn_left->pn_type) {      switch (node->pn_left->pn_type) {
631      case TOK_IN:      case TOK_IN:
632        /* for/in */        /* for/in */
# Line 646  Line 649 
649          break;          break;
650  */  */
651        }        }
652        fprintf(f, " in ");        Stream_write_string(f, " in ");
653        instrument_expression(node->pn_left->pn_right, f);        instrument_expression(node->pn_left->pn_right, f);
654        break;        break;
655      case TOK_RESERVED:      case TOK_RESERVED:
# Line 660  Line 663 
663            instrument_expression(node->pn_left->pn_kid1, f);            instrument_expression(node->pn_left->pn_kid1, f);
664          }          }
665        }        }
666        fprintf(f, ";");        Stream_write_string(f, ";");
667        if (node->pn_left->pn_kid2) {        if (node->pn_left->pn_kid2) {
668          fputc(' ', f);          Stream_write_char(f, ' ');
669          instrument_expression(node->pn_left->pn_kid2, f);          instrument_expression(node->pn_left->pn_kid2, f);
670        }        }
671        fprintf(f, ";");        Stream_write_string(f, ";");
672        if (node->pn_left->pn_kid3) {        if (node->pn_left->pn_kid3) {
673          fputc(' ', f);          Stream_write_char(f, ' ');
674          instrument_expression(node->pn_left->pn_kid3, f);          instrument_expression(node->pn_left->pn_kid3, f);
675        }        }
676        break;        break;
# Line 675  Line 678 
678        abort();        abort();
679        break;        break;
680      }      }
681      fprintf(f, ") {\n");      Stream_write_string(f, ") {\n");
682      instrument_statement(node->pn_right, f, indent + 2);      instrument_statement(node->pn_right, f, indent + 2);
683      fprintf(f, "}\n");      Stream_write_string(f, "}\n");
684      break;      break;
685    case TOK_THROW:    case TOK_THROW:
686      assert(node->pn_arity == PN_UNARY);      assert(node->pn_arity == PN_UNARY);
687      fprintf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
688      fprintf(f, "throw ");      Stream_write_string(f, "throw ");
689      instrument_expression(node->pn_u.unary.kid, f);      instrument_expression(node->pn_u.unary.kid, f);
690      fprintf(f, ";\n");      Stream_write_string(f, ";\n");
691      break;      break;
692    case TOK_TRY:    case TOK_TRY:
693      fprintf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
694      fprintf(f, "try {\n");      Stream_write_string(f, "try {\n");
695      instrument_statement(node->pn_kid1, f, indent + 2);      instrument_statement(node->pn_kid1, f, indent + 2);
696      fprintf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
697      fprintf(f, "}\n");      Stream_write_string(f, "}\n");
698      {      {
699        for (JSParseNode * catch = node->pn_kid2; catch != NULL; catch = catch->pn_kid2) {        for (JSParseNode * catch = node->pn_kid2; catch != NULL; catch = catch->pn_kid2) {
700          assert(catch->pn_type == TOK_CATCH);          assert(catch->pn_type == TOK_CATCH);
701          fprintf(f, "%*s", indent, "");          Stream_printf(f, "%*s", indent, "");
702          fprintf(f, "catch (");          Stream_write_string(f, "catch (");
703          assert(catch->pn_kid1->pn_arity == PN_NAME);          assert(catch->pn_kid1->pn_arity == PN_NAME);
704          print_string_atom(catch->pn_kid1->pn_atom, f);          print_string_atom(catch->pn_kid1->pn_atom, f);
705          if (catch->pn_kid1->pn_expr) {          if (catch->pn_kid1->pn_expr) {
706            fprintf(f, " if ");            Stream_write_string(f, " if ");
707            instrument_expression(catch->pn_kid1->pn_expr, f);            instrument_expression(catch->pn_kid1->pn_expr, f);
708          }          }
709          fprintf(f, ") {\n");          Stream_write_string(f, ") {\n");
710          instrument_statement(catch->pn_kid3, f, indent + 2);          instrument_statement(catch->pn_kid3, f, indent + 2);
711          fprintf(f, "%*s", indent, "");          Stream_printf(f, "%*s", indent, "");
712          fprintf(f, "}\n");          Stream_write_string(f, "}\n");
713        }        }
714      }      }
715      if (node->pn_kid3) {      if (node->pn_kid3) {
716        fprintf(f, "%*s", indent, "");        Stream_printf(f, "%*s", indent, "");
717        fprintf(f, "finally {\n");        Stream_write_string(f, "finally {\n");
718        instrument_statement(node->pn_kid3, f, indent + 2);        instrument_statement(node->pn_kid3, f, indent + 2);
719        fprintf(f, "%*s", indent, "");        Stream_printf(f, "%*s", indent, "");
720        fprintf(f, "}\n");        Stream_write_string(f, "}\n");
721      }      }
722      break;      break;
723    case TOK_CATCH:    case TOK_CATCH:
# Line 723  Line 726 
726    case TOK_BREAK:    case TOK_BREAK:
727    case TOK_CONTINUE:    case TOK_CONTINUE:
728      assert(node->pn_arity == PN_NAME || node->pn_arity == PN_NULLARY);      assert(node->pn_arity == PN_NAME || node->pn_arity == PN_NULLARY);
729      fprintf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
730      fputs(node->pn_type == TOK_BREAK? "break": "continue", f);      Stream_write_string(f, node->pn_type == TOK_BREAK? "break": "continue");
731      JSAtom * atom = node->pn_u.name.atom;      JSAtom * atom = node->pn_u.name.atom;
732      if (atom != NULL) {      if (atom != NULL) {
733        fputc(' ', f);        Stream_write_char(f, ' ');
734        print_string_atom(node->pn_atom, f);        print_string_atom(node->pn_atom, f);
735      }      }
736      fprintf(f, ";\n");      Stream_write_string(f, ";\n");
737      break;      break;
738    case TOK_WITH:    case TOK_WITH:
739      assert(node->pn_arity == PN_BINARY);      assert(node->pn_arity == PN_BINARY);
740      fprintf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
741      fprintf(f, "with (");      Stream_write_string(f, "with (");
742      instrument_expression(node->pn_left, f);      instrument_expression(node->pn_left, f);
743      fprintf(f, ") {\n");      Stream_write_string(f, ") {\n");
744      instrument_statement(node->pn_right, f, indent + 2);      instrument_statement(node->pn_right, f, indent + 2);
745      fprintf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
746      fprintf(f, "}\n");      Stream_write_string(f, "}\n");
747      break;      break;
748    case TOK_VAR:    case TOK_VAR:
749      instrument_var_statement(node, f, indent);      instrument_var_statement(node, f, indent);
750      fprintf(f, ";\n");      Stream_write_string(f, ";\n");
751      break;      break;
752    case TOK_RETURN:    case TOK_RETURN:
753      assert(node->pn_arity == PN_UNARY);      assert(node->pn_arity == PN_UNARY);
754      fprintf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
755      fprintf(f, "return");      Stream_write_string(f, "return");
756      if (node->pn_kid != NULL) {      if (node->pn_kid != NULL) {
757        fprintf(f, " ");        Stream_write_char(f, ' ');
758        instrument_expression(node->pn_kid, f);        instrument_expression(node->pn_kid, f);
759      }      }
760      fprintf(f, ";\n");      Stream_write_string(f, ";\n");
761      break;      break;
762    case TOK_SEMI:    case TOK_SEMI:
763      assert(node->pn_arity == PN_UNARY);      assert(node->pn_arity == PN_UNARY);
764      fprintf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
765      if (node->pn_kid != NULL) {      if (node->pn_kid != NULL) {
766        instrument_expression(node->pn_kid, f);        instrument_expression(node->pn_kid, f);
767      }      }
768      fprintf(f, ";\n");      Stream_write_string(f, ";\n");
769      break;      break;
770    case TOK_COLON:    case TOK_COLON:
771      assert(node->pn_arity == PN_NAME);      assert(node->pn_arity == PN_NAME);
# Line 770  Line 773 
773      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
774      statement it's supposed to label ...      statement it's supposed to label ...
775      */      */
776      fprintf(f, "%*s", indent < 2? 0: indent - 2, "");      Stream_printf(f, "%*s", indent < 2? 0: indent - 2, "");
777      print_string_atom(node->pn_atom, f);      print_string_atom(node->pn_atom, f);
778      fprintf(f, ":\n");      Stream_write_string(f, ":\n");
779      /*      /*
780      ... use output_statement instead of instrument_statement.      ... use output_statement instead of instrument_statement.
781      */      */
# Line 788  Line 791 
791  TOK_FUNCTION is handled as a statement and as an expression.  TOK_FUNCTION is handled as a statement and as an expression.
792  TOK_EXPORT, TOK_IMPORT are not handled.  TOK_EXPORT, TOK_IMPORT are not handled.
793  */  */
794  static void instrument_statement(JSParseNode * node, FILE * f, int indent) {  static void instrument_statement(JSParseNode * node, Stream * f, int indent) {
795    if (node->pn_type != TOK_LC) {    if (node->pn_type != TOK_LC) {
796      int line = node->pn_pos.begin.lineno;      int line = node->pn_pos.begin.lineno;
797      /* the root node has line number 0 */      /* the root node has line number 0 */
798      if (line != 0) {      if (line != 0) {
799        fprintf(f, "%*s", indent, "");        Stream_printf(f, "%*s", indent, "");
800        fprintf(f, "_$jscoverage['%s'][%d]++;\n", file_id, line);        Stream_printf(f, "_$jscoverage['%s'][%d]++;\n", file_id, line);
801        lines[line - 1] = 1;        lines[line - 1] = 1;
802      }      }
803    }    }
804    output_statement(node, f, indent);    output_statement(node, f, indent);
805  }  }
806    
807  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) {
808    file_id = id;    file_id = id;
809    
810    /* scan the javascript */    /* scan the javascript */
811    JSTokenStream * token_stream = js_NewFileTokenStream(context, NULL, input);    size_t input_length = input->length;
812      jschar * base = js_InflateString(context, (char *) input->data, &input_length);
813      if (base == NULL) {
814        fatal("out of memory");
815      }
816      JSTokenStream * token_stream = js_NewTokenStream(context, base, input_length, NULL, 1, NULL);
817    if (token_stream == NULL) {    if (token_stream == NULL) {
818      fatal("cannot create token stream from file: %s", file_id);      fatal("cannot create token stream from file: %s", file_id);
819    }    }
# Line 822  Line 830 
830    }    }
831    
832    /*    /*
833    Create a temporary file - we can't write directly to the output because we    An instrumented JavaScript file has 3 sections:
834    need to know the line number info first.    1. initialization
835      2. instrumented source code
836      3. original source code
837    */    */
   FILE * temporary = fopen(temporary_file_name, "w+");  
   if (temporary == NULL) {  
     fatal("cannot create temporary file for script: %s", file_id);  
   }  
838    
839    /* write instrumented javascript to the temporary */    Stream * instrumented = Stream_new(0);
840    instrument_statement(node, temporary, 0);    instrument_statement(node, instrumented, 0);
841    
842    /* write line number info to the output */    /* write line number info to the output */
843    fprintf(output, "/* automatically generated by JSCoverage - do not edit */\n");    Stream_write_string(output, "/* automatically generated by JSCoverage - do not edit */\n");
844    fprintf(output, "if (! top._$jscoverage) {\n  top._$jscoverage = {};\n}\n");    Stream_write_string(output, "if (! top._$jscoverage) {\n  top._$jscoverage = {};\n}\n");
845    fprintf(output, "var _$jscoverage = top._$jscoverage;\n");    Stream_write_string(output, "var _$jscoverage = top._$jscoverage;\n");
846    fprintf(output, "if (! _$jscoverage['%s']) {\n", file_id);    Stream_printf(output, "if (! _$jscoverage['%s']) {\n", file_id);
847    fprintf(output, "  _$jscoverage['%s'] = [];\n", file_id);    Stream_printf(output, "  _$jscoverage['%s'] = [];\n", file_id);
848    for (int i = 0; i < num_lines; i++) {    for (int i = 0; i < num_lines; i++) {
849      if (lines[i]) {      if (lines[i]) {
850        fprintf(output, "  _$jscoverage['%s'][%d] = 0;\n", file_id, i + 1);        Stream_printf(output, "  _$jscoverage['%s'][%d] = 0;\n", file_id, i + 1);
851      }      }
852    }    }
853    fprintf(output, "}\n");    Stream_write_string(output, "}\n");
854      free(lines);
855    lines = NULL;    lines = NULL;
856    
857    /* copy the temporary to the output */    /* copy the instrumented source code to the output */
858    fseek(temporary, 0, SEEK_SET);    Stream_write(output, instrumented->data, instrumented->length);
859    copy_stream(temporary, output);    Stream_write_char(output, '\n');
860    
861      /* copy the original source to the output */
862      size_t i = 0;
863      while (i < input_length) {
864        Stream_write_string(output, "// ");
865        size_t line_start = i;
866        while (i < input_length && base[i] != '\r' && base[i] != '\n') {
867          i++;
868        }
869    
870        size_t line_end = i;
871        if (i < input_length) {
872          if (base[i] == '\r') {
873            line_end = i;
874            i++;
875            if (i < input_length && base[i] == '\n') {
876              i++;
877            }
878          }
879          else if (base[i] == '\n') {
880            line_end = i;
881            i++;
882          }
883          else {
884            abort();
885          }
886        }
887    
888        char * line = js_DeflateString(context, base + line_start, line_end - line_start);
889        Stream_write_string(output, line);
890        Stream_write_char(output, '\n');
891        JS_free(context, line);
892      }
893    
894      Stream_delete(instrumented);
895    
896    fclose(temporary);    JS_free(context, base);
897    
898    file_id = NULL;    file_id = NULL;
899  }  }
900    
901  void jscoverage_instrument_js(const char * id, FILE * input, FILE * output, const char * temporary_file_name) {  void jscoverage_copy_resources(const char * destination_directory) {
902    instrument_js_stream(id, 0, input, output, temporary_file_name);    copy_resource("jscoverage.html", destination_directory);
903      copy_resource("jscoverage.css", destination_directory);
904      copy_resource("jscoverage.js", destination_directory);
905      copy_resource("jscoverage-throbber.gif", destination_directory);
906      copy_resource("jscoverage-sh_main.js", destination_directory);
907      copy_resource("jscoverage-sh_javascript.js", destination_directory);
908      copy_resource("jscoverage-sh_nedit.css", destination_directory);
909    }
910    
911    /*
912    coverage reports
913    */
914    
915    struct FileCoverageList {
916      FileCoverage * file_coverage;
917      struct FileCoverageList * next;
918    };
919    
920    struct Coverage {
921      JSHashTable * coverage_table;
922      struct FileCoverageList * coverage_list;
923    };
924    
925    static int compare_strings(const void * p1, const void * p2) {
926      return strcmp(p1, p2) == 0;
927    }
928    
929    Coverage * Coverage_new(void) {
930      Coverage * result = xmalloc(sizeof(Coverage));
931      result->coverage_table = JS_NewHashTable(1024, JS_HashString, compare_strings, NULL, NULL, NULL);
932      if (result->coverage_table == NULL) {
933        fatal("cannot create hash table");
934      }
935      result->coverage_list = NULL;
936      return result;
937    }
938    
939    void Coverage_delete(Coverage * coverage) {
940      JS_HashTableDestroy(coverage->coverage_table);
941      struct FileCoverageList * p = coverage->coverage_list;
942      while (p != NULL) {
943        free(p->file_coverage->lines);
944        free(p->file_coverage->source);
945        free(p->file_coverage->id);
946        free(p->file_coverage);
947        struct FileCoverageList * q = p;
948        p = p->next;
949        free(q);
950      }
951      free(coverage);
952    }
953    
954    struct EnumeratorArg {
955      CoverageForeachFunction f;
956      void * p;
957    };
958    
959    static intN enumerator(JSHashEntry * entry, intN i, void * arg) {
960      struct EnumeratorArg * enumerator_arg = arg;
961      enumerator_arg->f(entry->value, i, enumerator_arg->p);
962      return 0;
963    }
964    
965    void Coverage_foreach_file(Coverage * coverage, CoverageForeachFunction f, void * p) {
966      struct EnumeratorArg enumerator_arg;
967      enumerator_arg.f = f;
968      enumerator_arg.p = p;
969      JS_HashTableEnumerateEntries(coverage->coverage_table, enumerator, &enumerator_arg);
970    }
971    
972    int jscoverage_parse_json(Coverage * coverage, const uint8_t * json, size_t length) {
973      jschar * base = js_InflateString(context, (char *) json, &length);
974      if (base == NULL) {
975        fatal("out of memory");
976      }
977    
978      jschar * parenthesized_json = xnew(jschar, addst(length, 2));
979      parenthesized_json[0] = '(';
980      memcpy(parenthesized_json + 1, base, mulst(length, sizeof(jschar)));
981      parenthesized_json[length + 1] = ')';
982    
983      JS_free(context, base);
984    
985      JSTokenStream * token_stream = js_NewTokenStream(context, parenthesized_json, length + 2, NULL, 1, NULL);
986      if (token_stream == NULL) {
987        fatal("cannot create token stream");
988      }
989    
990      JSParseNode * root = js_ParseTokenStream(context, global, token_stream);
991      free(parenthesized_json);
992      if (root == NULL) {
993        return -1;
994      }
995    
996      /* root node must be TOK_LC */
997      if (root->pn_type != TOK_LC) {
998        return -1;
999      }
1000      JSParseNode * semi = root->pn_u.list.head;
1001    
1002      /* the list must be TOK_SEMI and it must contain only one element */
1003      if (semi->pn_type != TOK_SEMI || semi->pn_next != NULL) {
1004        return -1;
1005      }
1006      JSParseNode * parenthesized = semi->pn_kid;
1007    
1008      /* this must be a parenthesized expression */
1009      if (parenthesized->pn_type != TOK_RP) {
1010        return -1;
1011      }
1012      JSParseNode * object = parenthesized->pn_kid;
1013    
1014      /* this must be an object literal */
1015      if (object->pn_type != TOK_RC) {
1016        return -1;
1017      }
1018    
1019      for (JSParseNode * p = object->pn_head; p != NULL; p = p->pn_next) {
1020        /* every element of this list must be TOK_COLON */
1021        if (p->pn_type != TOK_COLON) {
1022          return -1;
1023        }
1024    
1025        /* the key must be a string representing the file */
1026        JSParseNode * key = p->pn_left;
1027        if (key->pn_type != TOK_STRING || ! ATOM_IS_STRING(key->pn_atom)) {
1028          return -1;
1029        }
1030        char * id_bytes = JS_GetStringBytes(ATOM_TO_STRING(key->pn_atom));
1031    
1032        /* the value must be an object literal OR an array */
1033        JSParseNode * value = p->pn_right;
1034        if (! (value->pn_type == TOK_RC || value->pn_type == TOK_RB)) {
1035          return -1;
1036        }
1037    
1038        JSParseNode * array = NULL;
1039        JSParseNode * source = NULL;
1040        if (value->pn_type == TOK_RB) {
1041          /* an array */
1042          array = value;
1043        }
1044        else if (value->pn_type == TOK_RC) {
1045          /* an object literal */
1046          if (value->pn_count != 2) {
1047            return -1;
1048          }
1049          for (JSParseNode * element = value->pn_head; element != NULL; element = element->pn_next) {
1050            if (element->pn_type != TOK_COLON) {
1051              return -1;
1052            }
1053            JSParseNode * left = element->pn_left;
1054            if (left->pn_type != TOK_STRING || ! ATOM_IS_STRING(left->pn_atom)) {
1055              return -1;
1056            }
1057            const char * s = JS_GetStringBytes(ATOM_TO_STRING(left->pn_atom));
1058            if (strcmp(s, "coverage") == 0) {
1059              array = element->pn_right;
1060              if (array->pn_type != TOK_RB) {
1061                return -1;
1062              }
1063            }
1064            else if (strcmp(s, "source") == 0) {
1065              source = element->pn_right;
1066              if (source->pn_type != TOK_STRING || ! ATOM_IS_STRING(source->pn_atom)) {
1067                return -1;
1068              }
1069            }
1070            else {
1071              return -1;
1072            }
1073          }
1074        }
1075        else {
1076          return -1;
1077        }
1078    
1079        if (array == NULL) {
1080          return -1;
1081        }
1082    
1083        /* look up the file in the coverage table */
1084        FileCoverage * file_coverage = JS_HashTableLookup(coverage->coverage_table, id_bytes);
1085        if (file_coverage == NULL) {
1086          /* not there: create a new one */
1087          char * id = xstrdup(id_bytes);
1088          file_coverage = xmalloc(sizeof(FileCoverage));
1089          file_coverage->id = id;
1090          file_coverage->num_lines = array->pn_count - 1;
1091          file_coverage->lines = xnew(int, array->pn_count);
1092          if (source == NULL) {
1093            file_coverage->source = NULL;
1094          }
1095          else {
1096            file_coverage->source = xstrdup(JS_GetStringBytes(ATOM_TO_STRING(source->pn_atom)));
1097          }
1098    
1099          /* set coverage for all lines */
1100          uint32 i = 0;
1101          for (JSParseNode * element = array->pn_head; element != NULL; element = element->pn_next, i++) {
1102            if (element->pn_type == TOK_NUMBER) {
1103              file_coverage->lines[i] = (int) element->pn_dval;
1104            }
1105            else if (element->pn_type == TOK_PRIMARY && element->pn_op == JSOP_NULL) {
1106              file_coverage->lines[i] = -1;
1107            }
1108            else {
1109              return -1;
1110            }
1111          }
1112          assert(i == array->pn_count);
1113    
1114          /* add to the hash table */
1115          JS_HashTableAdd(coverage->coverage_table, id, file_coverage);
1116          struct FileCoverageList * coverage_list = xmalloc(sizeof(struct FileCoverageList));
1117          coverage_list->file_coverage = file_coverage;
1118          coverage_list->next = coverage->coverage_list;
1119          coverage->coverage_list = coverage_list;
1120        }
1121        else {
1122          /* sanity check */
1123          assert(strcmp(file_coverage->id, id_bytes) == 0);
1124          if (file_coverage->num_lines != array->pn_count - 1) {
1125            return -2;
1126          }
1127    
1128          /* merge the coverage */
1129          uint32 i = 0;
1130          for (JSParseNode * element = array->pn_head; element != NULL; element = element->pn_next, i++) {
1131            if (element->pn_type == TOK_NUMBER) {
1132              if (file_coverage->lines[i] == -1) {
1133                return -2;
1134              }
1135              file_coverage->lines[i] += (int) element->pn_dval;
1136            }
1137            else if (element->pn_type == TOK_PRIMARY && element->pn_op == JSOP_NULL) {
1138              if (file_coverage->lines[i] != -1) {
1139                return -2;
1140              }
1141            }
1142            else {
1143              return -1;
1144            }
1145          }
1146          assert(i == array->pn_count);
1147    
1148          /* if this JSON file has source, use it */
1149          if (file_coverage->source == NULL && source != NULL) {
1150            file_coverage->source = xstrdup(JS_GetStringBytes(ATOM_TO_STRING(source->pn_atom)));
1151          }
1152        }
1153      }
1154    
1155      return 0;
1156  }  }

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

  ViewVC Help
Powered by ViewVC 1.1.24