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

Diff of /trunk/instrument-js.cpp

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

trunk/instrument-js.c revision 350 by siliconforks, Fri Oct 24 16:17:39 2008 UTC trunk/instrument-js.cpp revision 434 by siliconforks, Sun Mar 8 06:45:51 2009 UTC
# Line 1  Line 1 
1  /*  /*
2      instrument-js.c - JavaScript instrumentation routines      instrument-js.cpp - JavaScript instrumentation routines
3      Copyright (C) 2007, 2008 siliconforks.com      Copyright (C) 2007, 2008, 2009 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 22  Line 22 
22  #include "instrument-js.h"  #include "instrument-js.h"
23    
24  #include <assert.h>  #include <assert.h>
25    #include <math.h>
26  #include <stdlib.h>  #include <stdlib.h>
27  #include <string.h>  #include <string.h>
28    
29  #include <jsapi.h>  #include <jsapi.h>
30  #include <jsarena.h>  #include <jsarena.h>
31  #include <jsatom.h>  #include <jsatom.h>
32    #include <jsemit.h>
33  #include <jsexn.h>  #include <jsexn.h>
34  #include <jsfun.h>  #include <jsfun.h>
35  #include <jsinterp.h>  #include <jsinterp.h>
# Line 51  Line 53 
53    struct IfDirective * next;    struct IfDirective * next;
54  };  };
55    
56    enum JSCoverageMode jscoverage_mode = JSCOVERAGE_NORMAL;
57    
58  static bool * exclusive_directives = NULL;  static bool * exclusive_directives = NULL;
59    
60  static JSRuntime * runtime = NULL;  static JSRuntime * runtime = NULL;
# Line 67  Line 71 
71  static uint16_t num_lines = 0;  static uint16_t num_lines = 0;
72    
73  void jscoverage_set_js_version(const char * version) {  void jscoverage_set_js_version(const char * version) {
74    js_version = atoi(version);    js_version = JS_StringToVersion(version);
75      if (js_version != JSVERSION_UNKNOWN) {
76        return;
77      }
78    
79      char * end;
80      js_version = (JSVersion) strtol(version, &end, 10);
81      if ((size_t) (end - version) != strlen(version)) {
82        fatal("invalid version: %s", version);
83      }
84  }  }
85    
86  void jscoverage_init(void) {  void jscoverage_init(void) {
# Line 255  Line 268 
268    }    }
269  }  }
270    
271  static void instrument_expression(JSParseNode * node, Stream * f);  static void output_expression(JSParseNode * node, Stream * f, bool parenthesize_object_literals);
272  static void instrument_statement(JSParseNode * node, Stream * f, int indent, bool is_jscoverage_if);  static void instrument_statement(JSParseNode * node, Stream * f, int indent, bool is_jscoverage_if);
273  static void output_statement(JSParseNode * node, Stream * f, int indent, bool is_jscoverage_if);  static void output_statement(JSParseNode * node, Stream * f, int indent, bool is_jscoverage_if);
274    
# Line 272  Line 285 
285      Stream_write_string(f, "each ");      Stream_write_string(f, "each ");
286    }    }
287    Stream_write_char(f, '(');    Stream_write_char(f, '(');
288    instrument_expression(node->pn_left, f);    output_expression(node->pn_left, f, false);
289    Stream_write_char(f, ')');    Stream_write_char(f, ')');
290  }  }
291    
292    static void output_array_comprehension_or_generator_expression(JSParseNode * node, Stream * f) {
293      assert(node->pn_type == TOK_LEXICALSCOPE);
294      assert(node->pn_arity == PN_NAME);
295      JSParseNode * for_node = node->pn_expr;
296      assert(for_node->pn_type == TOK_FOR);
297      assert(for_node->pn_arity == PN_BINARY);
298      JSParseNode * p = for_node;
299      while (p->pn_type == TOK_FOR) {
300        p = p->pn_right;
301      }
302      JSParseNode * if_node = NULL;
303      if (p->pn_type == TOK_IF) {
304        if_node = p;
305        assert(if_node->pn_arity == PN_TERNARY);
306        p = if_node->pn_kid2;
307      }
308      switch (p->pn_arity) {
309      case PN_UNARY:
310        p = p->pn_kid;
311        if (p->pn_type == TOK_YIELD) {
312          /* for generator expressions */
313          p = p->pn_kid;
314        }
315        output_expression(p, f, false);
316        break;
317      case PN_LIST:
318        /*
319        When the array comprehension contains "if (0)", it will be optimized away and
320        the result will be an empty TOK_LC list.
321        */
322        assert(p->pn_type == TOK_LC);
323        assert(p->pn_head == NULL);
324        /* the "1" is arbitrary (since the list is empty) */
325        Stream_write_char(f, '1');
326        break;
327      default:
328        abort();
329        break;
330      }
331    
332      p = for_node;
333      while (p->pn_type == TOK_FOR) {
334        Stream_write_char(f, ' ');
335        output_for_in(p, f);
336        p = p->pn_right;
337      }
338      if (p->pn_type == TOK_LC) {
339        /* this is the optimized-away "if (0)" */
340        Stream_write_string(f, " if (0)");
341      }
342      else if (if_node) {
343        Stream_write_string(f, " if (");
344        output_expression(if_node->pn_kid1, f, false);
345        Stream_write_char(f, ')');
346      }
347    }
348    
349  static void instrument_function(JSParseNode * node, Stream * f, int indent, enum FunctionType type) {  static void instrument_function(JSParseNode * node, Stream * f, int indent, enum FunctionType type) {
350    assert(node->pn_type == TOK_FUNCTION);    assert(node->pn_type == TOK_FUNCTION);
351    assert(node->pn_arity == PN_FUNC);    assert(node->pn_arity == PN_FUNC);
# Line 286  Line 356 
356    assert(object == &function->object);    assert(object == &function->object);
357    Stream_printf(f, "%*s", indent, "");    Stream_printf(f, "%*s", indent, "");
358    if (type == FUNCTION_NORMAL) {    if (type == FUNCTION_NORMAL) {
359      Stream_write_string(f, "function");      Stream_write_string(f, "function ");
360    }    }
361    
362    /* function name */    /* function name */
363    if (function->atom) {    if (function->atom) {
     Stream_write_char(f, ' ');  
364      print_string_atom(function->atom, f);      print_string_atom(function->atom, f);
365    }    }
366    
# Line 299  Line 368 
368    function parameters - see JS_DecompileFunction in jsapi.cpp, which calls    function parameters - see JS_DecompileFunction in jsapi.cpp, which calls
369    js_DecompileFunction in jsopcode.cpp    js_DecompileFunction in jsopcode.cpp
370    */    */
371    Stream_write_string(f, "(");    Stream_write_char(f, '(');
372    JSArenaPool pool;    JSArenaPool pool;
373    JS_INIT_ARENA_POOL(&pool, "instrument_function", 256, 1, &context->scriptStackQuota);    JS_INIT_ARENA_POOL(&pool, "instrument_function", 256, 1, &context->scriptStackQuota);
374    jsuword * local_names = NULL;    jsuword * local_names = NULL;
# Line 309  Line 378 
378        fatal("out of memory");        fatal("out of memory");
379      }      }
380    }    }
381      bool destructuring = false;
382    for (int i = 0; i < function->nargs; i++) {    for (int i = 0; i < function->nargs; i++) {
383      if (i > 0) {      if (i > 0) {
384        Stream_write_string(f, ", ");        Stream_write_string(f, ", ");
385      }      }
386      JSAtom * param = JS_LOCAL_NAME_TO_ATOM(local_names[i]);      JSAtom * param = JS_LOCAL_NAME_TO_ATOM(local_names[i]);
387      print_string_atom(param, f);      if (param == NULL) {
388          destructuring = true;
389          JSParseNode * expression = NULL;
390          assert(node->pn_body->pn_type == TOK_LC || node->pn_body->pn_type == TOK_SEQ);
391          JSParseNode * semi = node->pn_body->pn_head;
392          assert(semi->pn_type == TOK_SEMI);
393          JSParseNode * comma = semi->pn_kid;
394          assert(comma->pn_type == TOK_COMMA);
395          for (JSParseNode * p = comma->pn_head; p != NULL; p = p->pn_next) {
396            assert(p->pn_type == TOK_ASSIGN);
397            JSParseNode * rhs = p->pn_right;
398            assert(JSSTRING_LENGTH(ATOM_TO_STRING(rhs->pn_atom)) == 0);
399            if (rhs->pn_slot == i) {
400              expression = p->pn_left;
401              break;
402            }
403          }
404          assert(expression != NULL);
405          output_expression(expression, f, false);
406        }
407        else {
408          print_string_atom(param, f);
409        }
410    }    }
411    JS_FinishArenaPool(&pool);    JS_FinishArenaPool(&pool);
412    Stream_write_string(f, ") {\n");    Stream_write_string(f, ") {\n");
413    
414    /* function body */    /* function body */
415    if (function->flags & JSFUN_EXPR_CLOSURE) {    if (function->flags & JSFUN_EXPR_CLOSURE) {
416      /* expression closure */      /* expression closure - use output_statement instead of instrument_statement */
417      output_statement(node->pn_body, f, indent + 2, false);      if (node->pn_body->pn_type == TOK_SEQ) {
418          assert(node->pn_body->pn_arity == PN_LIST);
419          assert(node->pn_body->pn_count == 2);
420          output_statement(node->pn_body->pn_head->pn_next, f, indent + 2, false);
421        }
422        else {
423          output_statement(node->pn_body, f, indent + 2, false);
424        }
425    }    }
426    else {    else {
427      instrument_statement(node->pn_body, f, indent + 2, false);      assert(node->pn_body->pn_type == TOK_LC);
428        assert(node->pn_body->pn_arity == PN_LIST);
429        JSParseNode * p = node->pn_body->pn_head;
430        if (destructuring) {
431          p = p->pn_next;
432        }
433        for (; p != NULL; p = p->pn_next) {
434          instrument_statement(p, f, indent + 2, false);
435        }
436    }    }
437    
438    Stream_write_string(f, "}\n");    Stream_write_char(f, '}');
439  }  }
440    
441  static void instrument_function_call(JSParseNode * node, Stream * f) {  static void instrument_function_call(JSParseNode * node, Stream * f) {
442    if (node->pn_head->pn_type == TOK_FUNCTION) {    JSParseNode * function_node = node->pn_head;
443      /* it's a generator expression */    if (function_node->pn_type == TOK_FUNCTION) {
444      JSParseNode * function_node = node->pn_head;      JSObject * object = function_node->pn_funpob->object;
445      JSParseNode * lexical_scope_node = function_node->pn_body;      assert(JS_ObjectIsFunction(context, object));
446      assert(lexical_scope_node->pn_type == TOK_LEXICALSCOPE);      JSFunction * function = (JSFunction *) JS_GetPrivate(context, object);
447      assert(lexical_scope_node->pn_arity == PN_NAME);      assert(function);
448      JSParseNode * for_node = lexical_scope_node->pn_body;      assert(object == &function->object);
449      assert(for_node->pn_type == TOK_FOR);  
450      assert(for_node->pn_arity == PN_BINARY);      if (function_node->pn_flags & TCF_GENEXP_LAMBDA) {
451      JSParseNode * if_node = NULL;        /* it's a generator expression */
452      JSParseNode * semi_node;        Stream_write_char(f, '(');
453      switch (for_node->pn_right->pn_type) {        output_array_comprehension_or_generator_expression(function_node->pn_body, f);
     case TOK_SEMI:  
       semi_node = for_node->pn_right;  
       break;  
     case TOK_IF:  
       if_node = for_node->pn_right;  
       assert(if_node->pn_arity == PN_TERNARY);  
       semi_node = if_node->pn_kid2;  
       assert(semi_node->pn_type == TOK_SEMI);  
       break;  
     default:  
       abort();  
       break;  
     }  
     assert(semi_node->pn_arity == PN_UNARY);  
     JSParseNode * yield_node = semi_node->pn_kid;  
     assert(yield_node->pn_type == TOK_YIELD);  
     Stream_write_char(f, '(');  
     instrument_expression(yield_node->pn_kid, f);  
     Stream_write_char(f, ' ');  
     output_for_in(for_node, f);  
     if (if_node) {  
       Stream_write_string(f, " if (");  
       instrument_expression(if_node->pn_kid1, f);  
454        Stream_write_char(f, ')');        Stream_write_char(f, ')');
455          return;
456      }      }
     Stream_write_char(f, ')');  
457    }    }
458    else {    output_expression(function_node, f, false);
459      instrument_expression(node->pn_head, f);    Stream_write_char(f, '(');
460      Stream_write_char(f, '(');    for (struct JSParseNode * p = function_node->pn_next; p != NULL; p = p->pn_next) {
461      for (struct JSParseNode * p = node->pn_head->pn_next; p != NULL; p = p->pn_next) {      if (p != node->pn_head->pn_next) {
462        if (p != node->pn_head->pn_next) {        Stream_write_string(f, ", ");
         Stream_write_string(f, ", ");  
       }  
       instrument_expression(p, f);  
463      }      }
464      Stream_write_char(f, ')');      output_expression(p, f, false);
465    }    }
466      Stream_write_char(f, ')');
467  }  }
468    
469  static void instrument_declarations(JSParseNode * list, Stream * f) {  static void instrument_declarations(JSParseNode * list, Stream * f) {
470    assert(list->pn_arity == PN_LIST);    assert(list->pn_arity == PN_LIST);
471    for (JSParseNode * p = list->pn_head; p != NULL; p = p->pn_next) {    for (JSParseNode * p = list->pn_head; p != NULL; p = p->pn_next) {
472      switch (p->pn_type) {      if (p != list->pn_head) {
473      case TOK_NAME:        Stream_write_string(f, ", ");
       assert(p->pn_arity == PN_NAME);  
       if (p != list->pn_head) {  
         Stream_write_string(f, ", ");  
       }  
       print_string_atom(p->pn_atom, f);  
       if (p->pn_expr != NULL) {  
         Stream_write_string(f, " = ");  
         instrument_expression(p->pn_expr, f);  
       }  
       break;  
     case TOK_ASSIGN:  
     case TOK_RB:  
     case TOK_RC:  
       /* destructuring */  
       instrument_expression(p, f);  
       break;  
     default:  
       abort();  
       break;  
474      }      }
475        output_expression(p, f, false);
476    }    }
477  }  }
478    
# Line 425  Line 489 
489  TOK_INSTANCEOF  binary  TOK_INSTANCEOF  binary
490  TOK_IN          binary  TOK_IN          binary
491  */  */
492  static void instrument_expression(JSParseNode * node, Stream * f) {  static void output_expression(JSParseNode * node, Stream * f, bool parenthesize_object_literals) {
493    switch (node->pn_type) {    switch (node->pn_type) {
494    case TOK_FUNCTION:    case TOK_FUNCTION:
495        Stream_write_char(f, '(');
496      instrument_function(node, f, 0, FUNCTION_NORMAL);      instrument_function(node, f, 0, FUNCTION_NORMAL);
497        Stream_write_char(f, ')');
498      break;      break;
499    case TOK_COMMA:    case TOK_COMMA:
500      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) {
501        if (p != node->pn_head) {        if (p != node->pn_head) {
502          Stream_write_string(f, ", ");          Stream_write_string(f, ", ");
503        }        }
504        instrument_expression(p, f);        output_expression(p, f, parenthesize_object_literals);
505      }      }
506      break;      break;
507    case TOK_ASSIGN:    case TOK_ASSIGN:
508      instrument_expression(node->pn_left, f);      output_expression(node->pn_left, f, parenthesize_object_literals);
509      Stream_write_char(f, ' ');      Stream_write_char(f, ' ');
510      switch (node->pn_op) {      switch (node->pn_op) {
511      case JSOP_ADD:      case JSOP_ADD:
# Line 460  Line 526 
526        break;        break;
527      }      }
528      Stream_write_string(f, "= ");      Stream_write_string(f, "= ");
529      instrument_expression(node->pn_right, f);      output_expression(node->pn_right, f, false);
530      break;      break;
531    case TOK_HOOK:    case TOK_HOOK:
532      instrument_expression(node->pn_kid1, f);      output_expression(node->pn_kid1, f, parenthesize_object_literals);
533      Stream_write_string(f, "? ");      Stream_write_string(f, "? ");
534      instrument_expression(node->pn_kid2, f);      output_expression(node->pn_kid2, f, false);
535      Stream_write_string(f, ": ");      Stream_write_string(f, ": ");
536      instrument_expression(node->pn_kid3, f);      output_expression(node->pn_kid3, f, false);
537      break;      break;
538    case TOK_OR:    case TOK_OR:
539    case TOK_AND:    case TOK_AND:
# Line 483  Line 549 
549    case TOK_DIVOP:    case TOK_DIVOP:
550      switch (node->pn_arity) {      switch (node->pn_arity) {
551      case PN_BINARY:      case PN_BINARY:
552        instrument_expression(node->pn_left, f);        output_expression(node->pn_left, f, parenthesize_object_literals);
553        Stream_printf(f, " %s ", get_op(node->pn_op));        Stream_printf(f, " %s ", get_op(node->pn_op));
554        instrument_expression(node->pn_right, f);        output_expression(node->pn_right, f, false);
555        break;        break;
556      case PN_LIST:      case PN_LIST:
557        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) {
558          if (p != node->pn_head) {          if (p == node->pn_head) {
559              output_expression(p, f, parenthesize_object_literals);
560            }
561            else {
562            Stream_printf(f, " %s ", get_op(node->pn_op));            Stream_printf(f, " %s ", get_op(node->pn_op));
563              output_expression(p, f, false);
564          }          }
         instrument_expression(p, f);  
565        }        }
566        break;        break;
567      default:      default:
# Line 502  Line 571 
571    case TOK_UNARYOP:    case TOK_UNARYOP:
572      switch (node->pn_op) {      switch (node->pn_op) {
573      case JSOP_NEG:      case JSOP_NEG:
574        Stream_write_char(f, '-');        Stream_write_string(f, "- ");
575        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, false);
576        break;        break;
577      case JSOP_POS:      case JSOP_POS:
578        Stream_write_char(f, '+');        Stream_write_string(f, "+ ");
579        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, false);
580        break;        break;
581      case JSOP_NOT:      case JSOP_NOT:
582        Stream_write_char(f, '!');        Stream_write_string(f, "! ");
583        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, false);
584        break;        break;
585      case JSOP_BITNOT:      case JSOP_BITNOT:
586        Stream_write_char(f, '~');        Stream_write_string(f, "~ ");
587        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, false);
588        break;        break;
589      case JSOP_TYPEOF:      case JSOP_TYPEOF:
590        Stream_write_string(f, "typeof ");        Stream_write_string(f, "typeof ");
591        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, false);
592        break;        break;
593      case JSOP_VOID:      case JSOP_VOID:
594        Stream_write_string(f, "void ");        Stream_write_string(f, "void ");
595        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, false);
596        break;        break;
597      default:      default:
598        abort();        fatal_source(file_id, node->pn_pos.begin.lineno, "unknown operator (%d)", node->pn_op);
599        break;        break;
600      }      }
601      break;      break;
# Line 540  Line 609 
609      case JSOP_INCPROP:      case JSOP_INCPROP:
610      case JSOP_INCELEM:      case JSOP_INCELEM:
611        Stream_write_string(f, "++");        Stream_write_string(f, "++");
612        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, false);
613        break;        break;
614      case JSOP_DECNAME:      case JSOP_DECNAME:
615      case JSOP_DECPROP:      case JSOP_DECPROP:
616      case JSOP_DECELEM:      case JSOP_DECELEM:
617        Stream_write_string(f, "--");        Stream_write_string(f, "--");
618        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, false);
619        break;        break;
620      case JSOP_NAMEINC:      case JSOP_NAMEINC:
621      case JSOP_PROPINC:      case JSOP_PROPINC:
622      case JSOP_ELEMINC:      case JSOP_ELEMINC:
623        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, parenthesize_object_literals);
624        Stream_write_string(f, "++");        Stream_write_string(f, "++");
625        break;        break;
626      case JSOP_NAMEDEC:      case JSOP_NAMEDEC:
627      case JSOP_PROPDEC:      case JSOP_PROPDEC:
628      case JSOP_ELEMDEC:      case JSOP_ELEMDEC:
629        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, parenthesize_object_literals);
630        Stream_write_string(f, "--");        Stream_write_string(f, "--");
631        break;        break;
632      default:      default:
# Line 571  Line 640 
640      break;      break;
641    case TOK_DELETE:    case TOK_DELETE:
642      Stream_write_string(f, "delete ");      Stream_write_string(f, "delete ");
643      instrument_expression(node->pn_kid, f);      output_expression(node->pn_kid, f, false);
644      break;      break;
645    case TOK_DOT:    case TOK_DOT:
646        /* numeric literals must be parenthesized */
647        switch (node->pn_expr->pn_type) {
648        case TOK_NUMBER:
649          Stream_write_char(f, '(');
650          output_expression(node->pn_expr, f, false);
651          Stream_write_char(f, ')');
652          break;
653        default:
654          output_expression(node->pn_expr, f, true);
655          break;
656        }
657      /*      /*
658      This may have originally been x['foo-bar'].  Because the string 'foo-bar'      This may have originally been x['foo-bar'].  Because the string 'foo-bar'
659      contains illegal characters, we have to use the subscript syntax instead of      contains illegal characters, we have to use the subscript syntax instead of
660      the dot syntax.      the dot syntax.
661      */      */
     instrument_expression(node->pn_expr, f);  
662      assert(ATOM_IS_STRING(node->pn_atom));      assert(ATOM_IS_STRING(node->pn_atom));
663      {      {
664        JSString * s = ATOM_TO_STRING(node->pn_atom);        JSString * s = ATOM_TO_STRING(node->pn_atom);
# Line 608  Line 687 
687      }      }
688      break;      break;
689    case TOK_LB:    case TOK_LB:
690      instrument_expression(node->pn_left, f);      output_expression(node->pn_left, f, false);
691      Stream_write_char(f, '[');      Stream_write_char(f, '[');
692      instrument_expression(node->pn_right, f);      output_expression(node->pn_right, f, false);
693      Stream_write_char(f, ']');      Stream_write_char(f, ']');
694      break;      break;
695    case TOK_LP:    case TOK_LP:
# Line 624  Line 703 
703        }        }
704        /* TOK_COMMA is a special case: a hole in the array */        /* TOK_COMMA is a special case: a hole in the array */
705        if (p->pn_type != TOK_COMMA) {        if (p->pn_type != TOK_COMMA) {
706          instrument_expression(p, f);          output_expression(p, f, false);
707        }        }
708      }      }
709      if (node->pn_extra == PNX_ENDCOMMA) {      if (node->pn_extra == PNX_ENDCOMMA) {
# Line 633  Line 712 
712      Stream_write_char(f, ']');      Stream_write_char(f, ']');
713      break;      break;
714    case TOK_RC:    case TOK_RC:
715        if (parenthesize_object_literals) {
716          Stream_write_char(f, '(');
717        }
718      Stream_write_char(f, '{');      Stream_write_char(f, '{');
719      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) {
720        assert(p->pn_type == TOK_COLON);        if (p->pn_type != TOK_COLON) {
721            fatal_source(file_id, p->pn_pos.begin.lineno, "unsupported node type (%d)", p->pn_type);
722          }
723        if (p != node->pn_head) {        if (p != node->pn_head) {
724          Stream_write_string(f, ", ");          Stream_write_string(f, ", ");
725        }        }
# Line 650  Line 734 
734          else {          else {
735            Stream_write_string(f, "set ");            Stream_write_string(f, "set ");
736          }          }
737          instrument_expression(p->pn_left, f);          output_expression(p->pn_left, f, false);
738            Stream_write_char(f, ' ');
739          if (p->pn_right->pn_type != TOK_FUNCTION) {          if (p->pn_right->pn_type != TOK_FUNCTION) {
740            fatal("parse error: expected function");            fatal_source(file_id, p->pn_pos.begin.lineno, "expected function");
741          }          }
742          instrument_function(p->pn_right, f, 0, FUNCTION_GETTER_OR_SETTER);          instrument_function(p->pn_right, f, 0, FUNCTION_GETTER_OR_SETTER);
743          break;          break;
744        default:        default:
745          instrument_expression(p->pn_left, f);          output_expression(p->pn_left, f, false);
746          Stream_write_string(f, ": ");          Stream_write_string(f, ": ");
747          instrument_expression(p->pn_right, f);          output_expression(p->pn_right, f, false);
748          break;          break;
749        }        }
750      }      }
751      Stream_write_char(f, '}');      Stream_write_char(f, '}');
752        if (parenthesize_object_literals) {
753          Stream_write_char(f, ')');
754        }
755      break;      break;
756    case TOK_RP:    case TOK_RP:
757      Stream_write_char(f, '(');      Stream_write_char(f, '(');
758      instrument_expression(node->pn_kid, f);      output_expression(node->pn_kid, f, false);
759      Stream_write_char(f, ')');      Stream_write_char(f, ')');
760      break;      break;
761    case TOK_NAME:    case TOK_NAME:
762      print_string_atom(node->pn_atom, f);      print_string_atom(node->pn_atom, f);
763        if (node->pn_expr != NULL) {
764          Stream_write_string(f, " = ");
765          output_expression(node->pn_expr, f, false);
766        }
767      break;      break;
768    case TOK_STRING:    case TOK_STRING:
769      print_quoted_string_atom(node->pn_atom, f);      print_quoted_string_atom(node->pn_atom, f);
# Line 693  Line 785 
785      To keep the output simple, special-case zero.      To keep the output simple, special-case zero.
786      */      */
787      if (node->pn_dval == 0.0) {      if (node->pn_dval == 0.0) {
788        Stream_write_string(f, "0");        if (signbit(node->pn_dval)) {
789            Stream_write_string(f, "-0");
790          }
791          else {
792            Stream_write_string(f, "0");
793          }
794        }
795        else if (node->pn_dval == INFINITY) {
796          Stream_write_string(f, "Number.POSITIVE_INFINITY");
797        }
798        else if (node->pn_dval == -INFINITY) {
799          Stream_write_string(f, "Number.NEGATIVE_INFINITY");
800        }
801        else if (isnan(node->pn_dval)) {
802          Stream_write_string(f, "Number.NaN");
803      }      }
804      else {      else {
805        Stream_printf(f, "%.15g", node->pn_dval);        Stream_printf(f, "%.15g", node->pn_dval);
# Line 719  Line 825 
825      }      }
826      break;      break;
827    case TOK_INSTANCEOF:    case TOK_INSTANCEOF:
828      instrument_expression(node->pn_left, f);      output_expression(node->pn_left, f, parenthesize_object_literals);
829      Stream_write_string(f, " instanceof ");      Stream_write_string(f, " instanceof ");
830      instrument_expression(node->pn_right, f);      output_expression(node->pn_right, f, false);
831      break;      break;
832    case TOK_IN:    case TOK_IN:
833      instrument_expression(node->pn_left, f);      output_expression(node->pn_left, f, false);
834      Stream_write_string(f, " in ");      Stream_write_string(f, " in ");
835      instrument_expression(node->pn_right, f);      output_expression(node->pn_right, f, false);
836      break;      break;
837    case TOK_LEXICALSCOPE:    case TOK_LEXICALSCOPE:
838      assert(node->pn_arity == PN_NAME);      assert(node->pn_arity == PN_NAME);
# Line 737  Line 843 
843      assert(node->pn_expr->pn_left->pn_arity == PN_LIST);      assert(node->pn_expr->pn_left->pn_arity == PN_LIST);
844      instrument_declarations(node->pn_expr->pn_left, f);      instrument_declarations(node->pn_expr->pn_left, f);
845      Stream_write_string(f, ") ");      Stream_write_string(f, ") ");
846      instrument_expression(node->pn_expr->pn_right, f);      output_expression(node->pn_expr->pn_right, f, true);
847      break;      break;
848    case TOK_YIELD:    case TOK_YIELD:
849      assert(node->pn_arity == PN_UNARY);      assert(node->pn_arity == PN_UNARY);
850      Stream_write_string(f, "yield ");      Stream_write_string(f, "yield");
851      instrument_expression(node->pn_kid, f);      if (node->pn_kid != NULL) {
852          Stream_write_char(f, ' ');
853          output_expression(node->pn_kid, f, true);
854        }
855      break;      break;
856    case TOK_ARRAYCOMP:    case TOK_ARRAYCOMP:
857      assert(node->pn_arity == PN_LIST);      assert(node->pn_arity == PN_LIST);
# Line 759  Line 868 
868          abort();          abort();
869          break;          break;
870        }        }
       assert(block_node->pn_type == TOK_LEXICALSCOPE);  
       assert(block_node->pn_arity == PN_NAME);  
       JSParseNode * for_node = block_node->pn_expr;  
       assert(for_node->pn_type == TOK_FOR);  
       assert(for_node->pn_arity == PN_BINARY);  
       JSParseNode * if_node = NULL;  
       JSParseNode * push_node;  
       switch (for_node->pn_right->pn_type) {  
       case TOK_ARRAYPUSH:  
         push_node = for_node->pn_right;  
         assert(push_node->pn_arity == PN_UNARY);  
         break;  
       case TOK_IF:  
         if_node = for_node->pn_right;  
         assert(if_node->pn_arity == PN_TERNARY);  
         push_node = if_node->pn_kid2;  
         break;  
       default:  
         abort();  
         break;  
       }  
871        Stream_write_char(f, '[');        Stream_write_char(f, '[');
872        instrument_expression(push_node->pn_kid, f);        output_array_comprehension_or_generator_expression(block_node, f);
       Stream_write_char(f, ' ');  
       output_for_in(for_node, f);  
       if (if_node) {  
         Stream_write_string(f, " if (");  
         instrument_expression(if_node->pn_kid1, f);  
         Stream_write_char(f, ')');  
       }  
873        Stream_write_char(f, ']');        Stream_write_char(f, ']');
874      }      }
875      break;      break;
# Line 803  Line 884 
884      instrument_declarations(node, f);      instrument_declarations(node, f);
885      break;      break;
886    default:    default:
887      fatal("unsupported node type in file %s: %d", file_id, node->pn_type);      fatal_source(file_id, node->pn_pos.begin.lineno, "unsupported node type (%d)", node->pn_type);
888    }    }
889  }  }
890    
# Line 811  Line 892 
892    switch (node->pn_type) {    switch (node->pn_type) {
893    case TOK_FUNCTION:    case TOK_FUNCTION:
894      instrument_function(node, f, indent, FUNCTION_NORMAL);      instrument_function(node, f, indent, FUNCTION_NORMAL);
895        Stream_write_char(f, '\n');
896      break;      break;
897    case TOK_LC:    case TOK_LC:
898      assert(node->pn_arity == PN_LIST);      assert(node->pn_arity == PN_LIST);
# Line 832  Line 914 
914      uint16_t line = node->pn_pos.begin.lineno;      uint16_t line = node->pn_pos.begin.lineno;
915      if (! is_jscoverage_if) {      if (! is_jscoverage_if) {
916        if (line > num_lines) {        if (line > num_lines) {
917          fatal("%s: script contains more than 65,535 lines", file_id);          fatal("file %s contains more than 65,535 lines", file_id);
918        }        }
919        if (line >= 2 && exclusive_directives[line - 2]) {        if (line >= 2 && exclusive_directives[line - 2]) {
920          is_jscoverage_if = true;          is_jscoverage_if = true;
# Line 841  Line 923 
923    
924      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
925      Stream_write_string(f, "if (");      Stream_write_string(f, "if (");
926      instrument_expression(node->pn_kid1, f);      output_expression(node->pn_kid1, f, false);
927      Stream_write_string(f, ") {\n");      Stream_write_string(f, ") {\n");
928      if (is_jscoverage_if && node->pn_kid3) {      if (is_jscoverage_if && node->pn_kid3) {
929        uint16_t else_start = node->pn_kid3->pn_pos.begin.lineno;        uint16_t else_start = node->pn_kid3->pn_pos.begin.lineno;
# Line 878  Line 960 
960      assert(node->pn_arity == PN_BINARY);      assert(node->pn_arity == PN_BINARY);
961      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
962      Stream_write_string(f, "switch (");      Stream_write_string(f, "switch (");
963      instrument_expression(node->pn_left, f);      output_expression(node->pn_left, f, false);
964      Stream_write_string(f, ") {\n");      Stream_write_string(f, ") {\n");
965      {      {
966        JSParseNode * list = node->pn_right;        JSParseNode * list = node->pn_right;
# Line 890  Line 972 
972          switch (p->pn_type) {          switch (p->pn_type) {
973          case TOK_CASE:          case TOK_CASE:
974            Stream_write_string(f, "case ");            Stream_write_string(f, "case ");
975            instrument_expression(p->pn_left, f);            output_expression(p->pn_left, f, false);
976            Stream_write_string(f, ":\n");            Stream_write_string(f, ":\n");
977            break;            break;
978          case TOK_DEFAULT:          case TOK_DEFAULT:
# Line 914  Line 996 
996      assert(node->pn_arity == PN_BINARY);      assert(node->pn_arity == PN_BINARY);
997      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
998      Stream_write_string(f, "while (");      Stream_write_string(f, "while (");
999      instrument_expression(node->pn_left, f);      output_expression(node->pn_left, f, false);
1000      Stream_write_string(f, ") {\n");      Stream_write_string(f, ") {\n");
1001      instrument_statement(node->pn_right, f, indent + 2, false);      instrument_statement(node->pn_right, f, indent + 2, false);
1002      Stream_write_string(f, "}\n");      Stream_write_string(f, "}\n");
# Line 927  Line 1009 
1009      Stream_write_string(f, "}\n");      Stream_write_string(f, "}\n");
1010      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
1011      Stream_write_string(f, "while (");      Stream_write_string(f, "while (");
1012      instrument_expression(node->pn_right, f);      output_expression(node->pn_right, f, false);
1013      Stream_write_string(f, ");\n");      Stream_write_string(f, ");\n");
1014      break;      break;
1015    case TOK_FOR:    case TOK_FOR:
# Line 939  Line 1021 
1021        assert(node->pn_left->pn_arity == PN_BINARY);        assert(node->pn_left->pn_arity == PN_BINARY);
1022        output_for_in(node, f);        output_for_in(node, f);
1023        break;        break;
1024      case TOK_RESERVED:      case TOK_FORHEAD:
1025        /* for (;;) */        /* for (;;) */
1026        assert(node->pn_left->pn_arity == PN_TERNARY);        assert(node->pn_left->pn_arity == PN_TERNARY);
1027        Stream_write_string(f, "for (");        Stream_write_string(f, "for (");
1028        if (node->pn_left->pn_kid1) {        if (node->pn_left->pn_kid1) {
1029          instrument_expression(node->pn_left->pn_kid1, f);          output_expression(node->pn_left->pn_kid1, f, false);
1030        }        }
1031        Stream_write_string(f, ";");        Stream_write_string(f, ";");
1032        if (node->pn_left->pn_kid2) {        if (node->pn_left->pn_kid2) {
1033          Stream_write_char(f, ' ');          Stream_write_char(f, ' ');
1034          instrument_expression(node->pn_left->pn_kid2, f);          output_expression(node->pn_left->pn_kid2, f, false);
1035        }        }
1036        Stream_write_string(f, ";");        Stream_write_string(f, ";");
1037        if (node->pn_left->pn_kid3) {        if (node->pn_left->pn_kid3) {
1038          Stream_write_char(f, ' ');          Stream_write_char(f, ' ');
1039          instrument_expression(node->pn_left->pn_kid3, f);          output_expression(node->pn_left->pn_kid3, f, false);
1040        }        }
1041        Stream_write_char(f, ')');        Stream_write_char(f, ')');
1042        break;        break;
# Line 970  Line 1052 
1052      assert(node->pn_arity == PN_UNARY);      assert(node->pn_arity == PN_UNARY);
1053      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
1054      Stream_write_string(f, "throw ");      Stream_write_string(f, "throw ");
1055      instrument_expression(node->pn_u.unary.kid, f);      output_expression(node->pn_u.unary.kid, f, false);
1056      Stream_write_string(f, ";\n");      Stream_write_string(f, ";\n");
1057      break;      break;
1058    case TOK_TRY:    case TOK_TRY:
# Line 983  Line 1065 
1065        assert(node->pn_kid2->pn_type == TOK_RESERVED);        assert(node->pn_kid2->pn_type == TOK_RESERVED);
1066        for (JSParseNode * scope = node->pn_kid2->pn_head; scope != NULL; scope = scope->pn_next) {        for (JSParseNode * scope = node->pn_kid2->pn_head; scope != NULL; scope = scope->pn_next) {
1067          assert(scope->pn_type == TOK_LEXICALSCOPE);          assert(scope->pn_type == TOK_LEXICALSCOPE);
1068          JSParseNode * catch = scope->pn_expr;          JSParseNode * catch_node = scope->pn_expr;
1069          assert(catch->pn_type == TOK_CATCH);          assert(catch_node->pn_type == TOK_CATCH);
1070          Stream_printf(f, "%*s", indent, "");          Stream_printf(f, "%*s", indent, "");
1071          Stream_write_string(f, "catch (");          Stream_write_string(f, "catch (");
1072          /* this may not be a name - destructuring assignment */          output_expression(catch_node->pn_kid1, f, false);
1073          /*          if (catch_node->pn_kid2) {
         assert(catch->pn_kid1->pn_arity == PN_NAME);  
         print_string_atom(catch->pn_kid1->pn_atom, f);  
         */  
         instrument_expression(catch->pn_kid1, f);  
         if (catch->pn_kid2) {  
1074            Stream_write_string(f, " if ");            Stream_write_string(f, " if ");
1075            instrument_expression(catch->pn_kid2, f);            output_expression(catch_node->pn_kid2, f, false);
1076          }          }
1077          Stream_write_string(f, ") {\n");          Stream_write_string(f, ") {\n");
1078          instrument_statement(catch->pn_kid3, f, indent + 2, false);          instrument_statement(catch_node->pn_kid3, f, indent + 2, false);
1079          Stream_printf(f, "%*s", indent, "");          Stream_printf(f, "%*s", indent, "");
1080          Stream_write_string(f, "}\n");          Stream_write_string(f, "}\n");
1081        }        }
# Line 1019  Line 1096 
1096      assert(node->pn_arity == PN_NAME || node->pn_arity == PN_NULLARY);      assert(node->pn_arity == PN_NAME || node->pn_arity == PN_NULLARY);
1097      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
1098      Stream_write_string(f, node->pn_type == TOK_BREAK? "break": "continue");      Stream_write_string(f, node->pn_type == TOK_BREAK? "break": "continue");
1099      JSAtom * atom = node->pn_u.name.atom;      if (node->pn_atom != NULL) {
     if (atom != NULL) {  
1100        Stream_write_char(f, ' ');        Stream_write_char(f, ' ');
1101        print_string_atom(node->pn_atom, f);        print_string_atom(node->pn_atom, f);
1102      }      }
# Line 1030  Line 1106 
1106      assert(node->pn_arity == PN_BINARY);      assert(node->pn_arity == PN_BINARY);
1107      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
1108      Stream_write_string(f, "with (");      Stream_write_string(f, "with (");
1109      instrument_expression(node->pn_left, f);      output_expression(node->pn_left, f, false);
1110      Stream_write_string(f, ") {\n");      Stream_write_string(f, ") {\n");
1111      instrument_statement(node->pn_right, f, indent + 2, false);      instrument_statement(node->pn_right, f, indent + 2, false);
1112      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
# Line 1038  Line 1114 
1114      break;      break;
1115    case TOK_VAR:    case TOK_VAR:
1116      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
1117      instrument_expression(node, f);      output_expression(node, f, false);
1118      Stream_write_string(f, ";\n");      Stream_write_string(f, ";\n");
1119      break;      break;
1120    case TOK_RETURN:    case TOK_RETURN:
# Line 1047  Line 1123 
1123      Stream_write_string(f, "return");      Stream_write_string(f, "return");
1124      if (node->pn_kid != NULL) {      if (node->pn_kid != NULL) {
1125        Stream_write_char(f, ' ');        Stream_write_char(f, ' ');
1126        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, true);
1127      }      }
1128      Stream_write_string(f, ";\n");      Stream_write_string(f, ";\n");
1129      break;      break;
# Line 1055  Line 1131 
1131      assert(node->pn_arity == PN_UNARY);      assert(node->pn_arity == PN_UNARY);
1132      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
1133      if (node->pn_kid != NULL) {      if (node->pn_kid != NULL) {
1134        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, true);
1135      }      }
1136      Stream_write_string(f, ";\n");      Stream_write_string(f, ";\n");
1137      break;      break;
1138    case TOK_COLON:    case TOK_COLON:
1139      {
1140      assert(node->pn_arity == PN_NAME);      assert(node->pn_arity == PN_NAME);
     /*  
     This one is tricky: can't output instrumentation between the label and the  
     statement it's supposed to label ...  
     */  
1141      Stream_printf(f, "%*s", indent < 2? 0: indent - 2, "");      Stream_printf(f, "%*s", indent < 2? 0: indent - 2, "");
1142      print_string_atom(node->pn_atom, f);      print_string_atom(node->pn_atom, f);
1143      Stream_write_string(f, ":\n");      Stream_write_string(f, ":\n");
1144      /*      JSParseNode * labelled = node->pn_expr;
1145      ... use output_statement instead of instrument_statement.      if (labelled->pn_type == TOK_LEXICALSCOPE) {
1146      */        labelled = labelled->pn_expr;
1147      output_statement(node->pn_expr, f, indent, false);      }
1148        if (labelled->pn_type == TOK_LC) {
1149          /* labelled block */
1150          Stream_printf(f, "%*s", indent, "");
1151          Stream_write_string(f, "{\n");
1152          instrument_statement(labelled, f, indent + 2, false);
1153          Stream_printf(f, "%*s", indent, "");
1154          Stream_write_string(f, "}\n");
1155        }
1156        else {
1157          /*
1158          This one is tricky: can't output instrumentation between the label and the
1159          statement it's supposed to label, so use output_statement instead of
1160          instrument_statement.
1161          */
1162          output_statement(labelled, f, indent, false);
1163        }
1164      break;      break;
1165      }
1166    case TOK_LEXICALSCOPE:    case TOK_LEXICALSCOPE:
1167      /* let statement */      /* let statement */
1168      assert(node->pn_arity == PN_NAME);      assert(node->pn_arity == PN_NAME);
# Line 1115  Line 1205 
1205      case PN_LIST:      case PN_LIST:
1206        /* let definition */        /* let definition */
1207        Stream_printf(f, "%*s", indent, "");        Stream_printf(f, "%*s", indent, "");
1208        instrument_expression(node, f);        output_expression(node, f, false);
1209        Stream_write_string(f, ";\n");        Stream_write_string(f, ";\n");
1210        break;        break;
1211      default:      default:
# Line 1127  Line 1217 
1217      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
1218      Stream_write_string(f, "debugger;\n");      Stream_write_string(f, "debugger;\n");
1219      break;      break;
1220      case TOK_SEQ:
1221        /*
1222        This occurs with the statement:
1223        for (var a = b in c) {}
1224        */
1225        assert(node->pn_arity == PN_LIST);
1226        for (JSParseNode * p = node->pn_head; p != NULL; p = p->pn_next) {
1227          instrument_statement(p, f, indent, false);
1228        }
1229        break;
1230    default:    default:
1231      fatal("unsupported node type in file %s: %d", file_id, node->pn_type);      fatal_source(file_id, node->pn_pos.begin.lineno, "unsupported node type (%d)", node->pn_type);
1232    }    }
1233  }  }
1234    
# Line 1141  Line 1241 
1241    if (node->pn_type != TOK_LC && node->pn_type != TOK_LEXICALSCOPE) {    if (node->pn_type != TOK_LC && node->pn_type != TOK_LEXICALSCOPE) {
1242      uint16_t line = node->pn_pos.begin.lineno;      uint16_t line = node->pn_pos.begin.lineno;
1243      if (line > num_lines) {      if (line > num_lines) {
1244        fatal("%s: script contains more than 65,535 lines", file_id);        fatal("file %s contains more than 65,535 lines", file_id);
1245      }      }
1246    
1247      /* the root node has line number 0 */      /* the root node has line number 0 */
# Line 1189  Line 1289 
1289  }  }
1290    
1291  static void error_reporter(JSContext * context, const char * message, JSErrorReport * report) {  static void error_reporter(JSContext * context, const char * message, JSErrorReport * report) {
1292    fprintf(stderr, "jscoverage: parse error: line %u: %s\n", report->lineno, message);    warn_source(file_id, report->lineno, "%s", message);
1293  }  }
1294    
1295  void jscoverage_instrument_js(const char * id, const uint16_t * characters, size_t num_characters, Stream * output) {  void jscoverage_instrument_js(const char * id, const uint16_t * characters, size_t num_characters, Stream * output) {
# Line 1198  Line 1298 
1298    /* parse the javascript */    /* parse the javascript */
1299    JSParseContext parse_context;    JSParseContext parse_context;
1300    if (! js_InitParseContext(context, &parse_context, NULL, NULL, characters, num_characters, NULL, NULL, 1)) {    if (! js_InitParseContext(context, &parse_context, NULL, NULL, characters, num_characters, NULL, NULL, 1)) {
1301      fatal("cannot create token stream from file: %s", file_id);      fatal("cannot create token stream from file %s", file_id);
1302    }    }
1303    JSErrorReporter old_error_reporter = JS_SetErrorReporter(context, error_reporter);    JSErrorReporter old_error_reporter = JS_SetErrorReporter(context, error_reporter);
1304    JSParseNode * node = js_ParseScript(context, global, &parse_context);    JSParseNode * node = js_ParseScript(context, global, &parse_context);
1305    if (node == NULL) {    if (node == NULL) {
1306      js_ReportUncaughtException(context);      js_ReportUncaughtException(context);
1307      fatal("parse error in file: %s", file_id);      fatal("parse error in file %s", file_id);
1308    }    }
1309    JS_SetErrorReporter(context, old_error_reporter);    JS_SetErrorReporter(context, old_error_reporter);
1310    num_lines = node->pn_pos.end.lineno;    num_lines = node->pn_pos.end.lineno;
1311    lines = xmalloc(num_lines);    lines = (char *) xmalloc(num_lines);
1312    for (unsigned int i = 0; i < num_lines; i++) {    for (unsigned int i = 0; i < num_lines; i++) {
1313      lines[i] = 0;      lines[i] = 0;
1314    }    }
# Line 1225  Line 1325 
1325    size_t i = 0;    size_t i = 0;
1326    while (i < num_characters) {    while (i < num_characters) {
1327      if (line_number == UINT16_MAX) {      if (line_number == UINT16_MAX) {
1328        fatal("%s: script has more than 65,535 lines", file_id);        fatal("file %s contains more than 65,535 lines", file_id);
1329      }      }
1330      line_number++;      line_number++;
1331      size_t line_start = i;      size_t line_start = i;
# Line 1292  Line 1392 
1392    
1393    /* write line number info to the output */    /* write line number info to the output */
1394    Stream_write_string(output, "/* automatically generated by JSCoverage - do not edit */\n");    Stream_write_string(output, "/* automatically generated by JSCoverage - do not edit */\n");
1395    Stream_write_string(output, "if (! top._$jscoverage) {\n  top._$jscoverage = {};\n}\n");    switch (jscoverage_mode) {
1396    Stream_write_string(output, "var _$jscoverage = top._$jscoverage;\n");    case JSCOVERAGE_MOZILLA:
1397        Stream_write_string(output, "try {\n");
1398        Stream_write_string(output, "  Components.utils.import('resource://gre/modules/jscoverage.jsm');\n");
1399        Stream_printf(output, "  dump('%s: successfully imported jscoverage module\\n');\n", id);
1400        Stream_write_string(output, "}\n");
1401        Stream_write_string(output, "catch (e) {\n");
1402        Stream_write_string(output, "  _$jscoverage = {};\n");
1403        Stream_printf(output, "  dump('%s: failed to import jscoverage module - coverage not available for this file\\n');\n", id);
1404        Stream_write_string(output, "}\n");
1405        break;
1406      case JSCOVERAGE_NORMAL:
1407        Stream_write_string(output, "if (! top._$jscoverage) {\n  top._$jscoverage = {};\n}\n");
1408        Stream_write_string(output, "var _$jscoverage = top._$jscoverage;\n");
1409        break;
1410      case JSCOVERAGE_NO_BROWSER:
1411        Stream_write_string(output, "if (typeof _$jscoverage === 'undefined') {\n  var _$jscoverage = {};\n}\n");
1412        break;
1413      }
1414    Stream_printf(output, "if (! _$jscoverage['%s']) {\n", file_id);    Stream_printf(output, "if (! _$jscoverage['%s']) {\n", file_id);
1415    Stream_printf(output, "  _$jscoverage['%s'] = [];\n", file_id);    Stream_printf(output, "  _$jscoverage['%s'] = [];\n", file_id);
1416    for (int i = 0; i < num_lines; i++) {    for (int i = 0; i < num_lines; i++) {
# Line 1307  Line 1424 
1424    free(exclusive_directives);    free(exclusive_directives);
1425    exclusive_directives = NULL;    exclusive_directives = NULL;
1426    
1427      /* copy the original source to the output */
1428      Stream_printf(output, "_$jscoverage['%s'].source = ", file_id);
1429      jscoverage_write_source(id, characters, num_characters, output);
1430      Stream_printf(output, ";\n");
1431    
1432    /* conditionals */    /* conditionals */
1433    if (has_conditionals) {    if (has_conditionals) {
1434      Stream_printf(output, "_$jscoverage['%s'].conditionals = [];\n", file_id);      Stream_printf(output, "_$jscoverage['%s'].conditionals = [];\n", file_id);
# Line 1331  Line 1453 
1453      free(if_directive);      free(if_directive);
1454    }    }
1455    
   /* copy the original source to the output */  
   Stream_printf(output, "_$jscoverage['%s'].source = ", file_id);  
   jscoverage_write_source(id, characters, num_characters, output);  
   Stream_printf(output, ";\n");  
   
1456    Stream_delete(instrumented);    Stream_delete(instrumented);
1457    
1458    file_id = NULL;    file_id = NULL;
# Line 1509  Line 1626 
1626  };  };
1627    
1628  static int compare_strings(const void * p1, const void * p2) {  static int compare_strings(const void * p1, const void * p2) {
1629    return strcmp(p1, p2) == 0;    return strcmp((const char *) p1, (const char *) p2) == 0;
1630  }  }
1631    
1632  Coverage * Coverage_new(void) {  Coverage * Coverage_new(void) {
1633    Coverage * result = xmalloc(sizeof(Coverage));    Coverage * result = (Coverage *) xmalloc(sizeof(Coverage));
1634    result->coverage_table = JS_NewHashTable(1024, JS_HashString, compare_strings, NULL, NULL, NULL);    result->coverage_table = JS_NewHashTable(1024, JS_HashString, compare_strings, NULL, NULL, NULL);
1635    if (result->coverage_table == NULL) {    if (result->coverage_table == NULL) {
1636      fatal("cannot create hash table");      fatal("cannot create hash table");
# Line 1548  Line 1665 
1665  };  };
1666    
1667  static intN enumerator(JSHashEntry * entry, intN i, void * arg) {  static intN enumerator(JSHashEntry * entry, intN i, void * arg) {
1668    struct EnumeratorArg * enumerator_arg = arg;    struct EnumeratorArg * enumerator_arg = (struct EnumeratorArg *) arg;
1669    enumerator_arg->f(entry->value, i, enumerator_arg->p);    enumerator_arg->f((FileCoverage *) entry->value, i, enumerator_arg->p);
1670    return 0;    return 0;
1671  }  }
1672    
# Line 1582  Line 1699 
1699    }    }
1700    JSParseNode * root = js_ParseScript(context, global, &parse_context);    JSParseNode * root = js_ParseScript(context, global, &parse_context);
1701    free(parenthesized_json);    free(parenthesized_json);
1702    
1703      JSParseNode * semi = NULL;
1704      JSParseNode * object = NULL;
1705    
1706    if (root == NULL) {    if (root == NULL) {
1707      result = -1;      result = -1;
1708      goto done;      goto done;
# Line 1592  Line 1713 
1713      result = -1;      result = -1;
1714      goto done;      goto done;
1715    }    }
1716    JSParseNode * semi = root->pn_u.list.head;    semi = root->pn_u.list.head;
1717    
1718    /* the list must be TOK_SEMI and it must contain only one element */    /* the list must be TOK_SEMI and it must contain only one element */
1719    if (semi->pn_type != TOK_SEMI || semi->pn_next != NULL) {    if (semi->pn_type != TOK_SEMI || semi->pn_next != NULL) {
1720      result = -1;      result = -1;
1721      goto done;      goto done;
1722    }    }
1723    JSParseNode * parenthesized = semi->pn_kid;    object = semi->pn_kid;
   
   /* this must be a parenthesized expression */  
   if (parenthesized->pn_type != TOK_RP) {  
     result = -1;  
     goto done;  
   }  
   JSParseNode * object = parenthesized->pn_kid;  
1724    
1725    /* this must be an object literal */    /* this must be an object literal */
1726    if (object->pn_type != TOK_RC) {    if (object->pn_type != TOK_RC) {
# Line 1690  Line 1804 
1804      }      }
1805    
1806      /* look up the file in the coverage table */      /* look up the file in the coverage table */
1807      FileCoverage * file_coverage = JS_HashTableLookup(coverage->coverage_table, id_bytes);      FileCoverage * file_coverage = (FileCoverage *) JS_HashTableLookup(coverage->coverage_table, id_bytes);
1808      if (file_coverage == NULL) {      if (file_coverage == NULL) {
1809        /* not there: create a new one */        /* not there: create a new one */
1810        char * id = xstrdup(id_bytes);        char * id = xstrdup(id_bytes);
1811        file_coverage = xmalloc(sizeof(FileCoverage));        file_coverage = (FileCoverage *) xmalloc(sizeof(FileCoverage));
1812        file_coverage->id = id;        file_coverage->id = id;
1813        file_coverage->num_coverage_lines = array->pn_count;        file_coverage->num_coverage_lines = array->pn_count;
1814        file_coverage->coverage_lines = xnew(int, array->pn_count);        file_coverage->coverage_lines = xnew(int, array->pn_count);
# Line 1718  Line 1832 
1832    
1833        /* add to the hash table */        /* add to the hash table */
1834        JS_HashTableAdd(coverage->coverage_table, id, file_coverage);        JS_HashTableAdd(coverage->coverage_table, id, file_coverage);
1835        struct FileCoverageList * coverage_list = xmalloc(sizeof(struct FileCoverageList));        struct FileCoverageList * coverage_list = (FileCoverageList *) xmalloc(sizeof(struct FileCoverageList));
1836        coverage_list->file_coverage = file_coverage;        coverage_list->file_coverage = file_coverage;
1837        coverage_list->next = coverage->coverage_list;        coverage_list->next = coverage->coverage_list;
1838        coverage->coverage_list = coverage_list;        coverage->coverage_list = coverage_list;

Legend:
Removed from v.350  
changed lines
  Added in v.434

  ViewVC Help
Powered by ViewVC 1.1.24