/[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 179 by siliconforks, Sun Sep 21 18:35:21 2008 UTC revision 245 by siliconforks, Sun Oct 5 18:05:12 2008 UTC
# Line 40  Line 40 
40  #include "resource-manager.h"  #include "resource-manager.h"
41  #include "util.h"  #include "util.h"
42    
43    struct IfDirective {
44      const jschar * condition_start;
45      const jschar * condition_end;
46      uint16_t start_line;
47      uint16_t end_line;
48      struct IfDirective * next;
49    };
50    
51    static bool * exclusive_directives = NULL;
52    
53  static JSRuntime * runtime = NULL;  static JSRuntime * runtime = NULL;
54  static JSContext * context = NULL;  static JSContext * context = NULL;
55  static JSObject * global = NULL;  static JSObject * global = NULL;
# Line 50  Line 60 
60  */  */
61  static const char * file_id = NULL;  static const char * file_id = NULL;
62  static char * lines = NULL;  static char * lines = NULL;
63    static uint16_t num_lines = 0;
64    
65  void jscoverage_init(void) {  void jscoverage_init(void) {
66    runtime = JS_NewRuntime(8L * 1024L * 1024L);    runtime = JS_NewRuntime(8L * 1024L * 1024L);
# Line 204  Line 215 
215  }  }
216    
217  static void instrument_expression(JSParseNode * node, Stream * f);  static void instrument_expression(JSParseNode * node, Stream * f);
218  static void instrument_statement(JSParseNode * node, Stream * f, int indent);  static void instrument_statement(JSParseNode * node, Stream * f, int indent, bool is_jscoverage_if);
219    
220  enum FunctionType {  enum FunctionType {
221    FUNCTION_NORMAL,    FUNCTION_NORMAL,
# Line 260  Line 271 
271    free(params);    free(params);
272    
273    /* function body */    /* function body */
274    instrument_statement(node->pn_body, f, indent + 2);    instrument_statement(node->pn_body, f, indent + 2, false);
275    
276    Stream_write_string(f, "}\n");    Stream_write_string(f, "}\n");
277  }  }
# Line 619  Line 630 
630    }    }
631  }  }
632    
633  static void output_statement(JSParseNode * node, Stream * f, int indent) {  static void output_statement(JSParseNode * node, Stream * f, int indent, bool is_jscoverage_if) {
634    switch (node->pn_type) {    switch (node->pn_type) {
635    case TOK_FUNCTION:    case TOK_FUNCTION:
636      instrument_function(node, f, indent, FUNCTION_NORMAL);      instrument_function(node, f, indent, FUNCTION_NORMAL);
# Line 630  Line 641 
641      Stream_write_string(f, "{\n");      Stream_write_string(f, "{\n");
642  */  */
643      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) {
644        instrument_statement(p, f, indent);        instrument_statement(p, f, indent, false);
645      }      }
646  /*  /*
647      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
# Line 638  Line 649 
649  */  */
650      break;      break;
651    case TOK_IF:    case TOK_IF:
652      {
653      assert(node->pn_arity == PN_TERNARY);      assert(node->pn_arity == PN_TERNARY);
654    
655        uint16_t line = node->pn_pos.begin.lineno;
656        if (! is_jscoverage_if) {
657          if (line > num_lines) {
658            fatal("%s: script contains more than 65,535 lines", file_id);
659          }
660          if (line >= 2 && exclusive_directives[line - 2]) {
661            is_jscoverage_if = true;
662          }
663        }
664    
665      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
666      Stream_write_string(f, "if (");      Stream_write_string(f, "if (");
667      instrument_expression(node->pn_kid1, f);      instrument_expression(node->pn_kid1, f);
668      Stream_write_string(f, ") {\n");      Stream_write_string(f, ") {\n");
669      instrument_statement(node->pn_kid2, f, indent + 2);      if (is_jscoverage_if && node->pn_kid3) {
670          uint16_t else_start = node->pn_kid3->pn_pos.begin.lineno;
671          uint16_t else_end = node->pn_kid3->pn_pos.end.lineno + 1;
672          Stream_printf(f, "%*s", indent + 2, "");
673          Stream_printf(f, "_$jscoverage['%s'].conditionals[%d] = %d;\n", file_id, else_start, else_end);
674        }
675        instrument_statement(node->pn_kid2, f, indent + 2, false);
676      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
677      Stream_write_string(f, "}\n");      Stream_write_string(f, "}\n");
678      if (node->pn_kid3) {  
679        if (node->pn_kid3 || is_jscoverage_if) {
680        Stream_printf(f, "%*s", indent, "");        Stream_printf(f, "%*s", indent, "");
681        Stream_write_string(f, "else {\n");        Stream_write_string(f, "else {\n");
682        instrument_statement(node->pn_kid3, f, indent + 2);  
683          if (is_jscoverage_if) {
684            uint16_t if_start = node->pn_kid2->pn_pos.begin.lineno + 1;
685            uint16_t if_end = node->pn_kid2->pn_pos.end.lineno + 1;
686            Stream_printf(f, "%*s", indent + 2, "");
687            Stream_printf(f, "_$jscoverage['%s'].conditionals[%d] = %d;\n", file_id, if_start, if_end);
688          }
689    
690          if (node->pn_kid3) {
691            instrument_statement(node->pn_kid3, f, indent + 2, is_jscoverage_if);
692          }
693    
694        Stream_printf(f, "%*s", indent, "");        Stream_printf(f, "%*s", indent, "");
695        Stream_write_string(f, "}\n");        Stream_write_string(f, "}\n");
696      }      }
697    
698      break;      break;
699      }
700    case TOK_SWITCH:    case TOK_SWITCH:
701      assert(node->pn_arity == PN_BINARY);      assert(node->pn_arity == PN_BINARY);
702      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
# Line 675  Line 718 
718          abort();          abort();
719          break;          break;
720        }        }
721        instrument_statement(p->pn_right, f, indent + 2);        instrument_statement(p->pn_right, f, indent + 2, false);
722      }      }
723      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
724      Stream_write_string(f, "}\n");      Stream_write_string(f, "}\n");
# Line 690  Line 733 
733      Stream_write_string(f, "while (");      Stream_write_string(f, "while (");
734      instrument_expression(node->pn_left, f);      instrument_expression(node->pn_left, f);
735      Stream_write_string(f, ") {\n");      Stream_write_string(f, ") {\n");
736      instrument_statement(node->pn_right, f, indent + 2);      instrument_statement(node->pn_right, f, indent + 2, false);
737      Stream_write_string(f, "}\n");      Stream_write_string(f, "}\n");
738      break;      break;
739    case TOK_DO:    case TOK_DO:
740      assert(node->pn_arity == PN_BINARY);      assert(node->pn_arity == PN_BINARY);
741      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
742      Stream_write_string(f, "do {\n");      Stream_write_string(f, "do {\n");
743      instrument_statement(node->pn_left, f, indent + 2);      instrument_statement(node->pn_left, f, indent + 2, false);
744      Stream_write_string(f, "}\n");      Stream_write_string(f, "}\n");
745      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
746      Stream_write_string(f, "while (");      Stream_write_string(f, "while (");
# Line 760  Line 803 
803        break;        break;
804      }      }
805      Stream_write_string(f, ") {\n");      Stream_write_string(f, ") {\n");
806      instrument_statement(node->pn_right, f, indent + 2);      instrument_statement(node->pn_right, f, indent + 2, false);
807      Stream_write_string(f, "}\n");      Stream_write_string(f, "}\n");
808      break;      break;
809    case TOK_THROW:    case TOK_THROW:
# Line 773  Line 816 
816    case TOK_TRY:    case TOK_TRY:
817      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
818      Stream_write_string(f, "try {\n");      Stream_write_string(f, "try {\n");
819      instrument_statement(node->pn_kid1, f, indent + 2);      instrument_statement(node->pn_kid1, f, indent + 2, false);
820      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
821      Stream_write_string(f, "}\n");      Stream_write_string(f, "}\n");
822      {      {
# Line 788  Line 831 
831            instrument_expression(catch->pn_kid1->pn_expr, f);            instrument_expression(catch->pn_kid1->pn_expr, f);
832          }          }
833          Stream_write_string(f, ") {\n");          Stream_write_string(f, ") {\n");
834          instrument_statement(catch->pn_kid3, f, indent + 2);          instrument_statement(catch->pn_kid3, f, indent + 2, false);
835          Stream_printf(f, "%*s", indent, "");          Stream_printf(f, "%*s", indent, "");
836          Stream_write_string(f, "}\n");          Stream_write_string(f, "}\n");
837        }        }
# Line 796  Line 839 
839      if (node->pn_kid3) {      if (node->pn_kid3) {
840        Stream_printf(f, "%*s", indent, "");        Stream_printf(f, "%*s", indent, "");
841        Stream_write_string(f, "finally {\n");        Stream_write_string(f, "finally {\n");
842        instrument_statement(node->pn_kid3, f, indent + 2);        instrument_statement(node->pn_kid3, f, indent + 2, false);
843        Stream_printf(f, "%*s", indent, "");        Stream_printf(f, "%*s", indent, "");
844        Stream_write_string(f, "}\n");        Stream_write_string(f, "}\n");
845      }      }
# Line 822  Line 865 
865      Stream_write_string(f, "with (");      Stream_write_string(f, "with (");
866      instrument_expression(node->pn_left, f);      instrument_expression(node->pn_left, f);
867      Stream_write_string(f, ") {\n");      Stream_write_string(f, ") {\n");
868      instrument_statement(node->pn_right, f, indent + 2);      instrument_statement(node->pn_right, f, indent + 2, false);
869      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
870      Stream_write_string(f, "}\n");      Stream_write_string(f, "}\n");
871      break;      break;
# Line 860  Line 903 
903      /*      /*
904      ... use output_statement instead of instrument_statement.      ... use output_statement instead of instrument_statement.
905      */      */
906      output_statement(node->pn_expr, f, indent);      output_statement(node->pn_expr, f, indent, false);
907      break;      break;
908    default:    default:
909      fatal("unsupported node type in file %s: %d", file_id, node->pn_type);      fatal("unsupported node type in file %s: %d", file_id, node->pn_type);
# Line 872  Line 915 
915  TOK_FUNCTION is handled as a statement and as an expression.  TOK_FUNCTION is handled as a statement and as an expression.
916  TOK_EXPORT, TOK_IMPORT are not handled.  TOK_EXPORT, TOK_IMPORT are not handled.
917  */  */
918  static void instrument_statement(JSParseNode * node, Stream * f, int indent) {  static void instrument_statement(JSParseNode * node, Stream * f, int indent, bool is_jscoverage_if) {
919    if (node->pn_type != TOK_LC) {    if (node->pn_type != TOK_LC) {
920      int line = node->pn_pos.begin.lineno;      uint16_t line = node->pn_pos.begin.lineno;
921        if (line > num_lines) {
922          fatal("%s: script contains more than 65,535 lines", file_id);
923        }
924    
925      /* the root node has line number 0 */      /* the root node has line number 0 */
926      if (line != 0) {      if (line != 0) {
927        Stream_printf(f, "%*s", indent, "");        Stream_printf(f, "%*s", indent, "");
# Line 882  Line 929 
929        lines[line - 1] = 1;        lines[line - 1] = 1;
930      }      }
931    }    }
932    output_statement(node, f, indent);    output_statement(node, f, indent, is_jscoverage_if);
933    }
934    
935    static bool characters_start_with(const jschar * characters, size_t line_start, size_t line_end, const char * prefix) {
936      const jschar * characters_end = characters + line_end;
937      const jschar * cp = characters + line_start;
938      const char * bp = prefix;
939      for (;;) {
940        if (*bp == '\0') {
941          return true;
942        }
943        else if (cp == characters_end) {
944          return false;
945        }
946        else if (*cp != *bp) {
947          return false;
948        }
949        bp++;
950        cp++;
951      }
952    }
953    
954    static bool characters_are_white_space(const jschar * characters, size_t line_start, size_t line_end) {
955      /* XXX - other Unicode space */
956      const jschar * end = characters + line_end;
957      for (const jschar * p = characters + line_start; p < end; p++) {
958        jschar c = *p;
959        if (c == 0x9 || c == 0xB || c == 0xC || c == 0x20 || c == 0xA0) {
960          continue;
961        }
962        else {
963          return false;
964        }
965      }
966      return true;
967  }  }
968    
969  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 899  Line 980 
980    if (node == NULL) {    if (node == NULL) {
981      fatal("parse error in file: %s", file_id);      fatal("parse error in file: %s", file_id);
982    }    }
983    int num_lines = node->pn_pos.end.lineno;    num_lines = node->pn_pos.end.lineno;
984    lines = xmalloc(num_lines);    lines = xmalloc(num_lines);
985    for (int i = 0; i < num_lines; i++) {    for (unsigned int i = 0; i < num_lines; i++) {
986      lines[i] = 0;      lines[i] = 0;
987    }    }
988    
989      /* search code for conditionals */
990      exclusive_directives = xnew(bool, num_lines);
991      for (unsigned int i = 0; i < num_lines; i++) {
992        exclusive_directives[i] = false;
993      }
994    
995      bool has_conditionals = false;
996      struct IfDirective * if_directives = NULL;
997      size_t line_number = 0;
998      size_t i = 0;
999      while (i < num_characters) {
1000        if (line_number == UINT16_MAX) {
1001          fatal("%s: script has more than 65,535 lines", file_id);
1002        }
1003        line_number++;
1004        size_t line_start = i;
1005        jschar c;
1006        bool done = false;
1007        while (! done && i < num_characters) {
1008          c = characters[i];
1009          switch (c) {
1010          case '\r':
1011          case '\n':
1012          case 0x2028:
1013          case 0x2029:
1014            done = true;
1015            break;
1016          default:
1017            i++;
1018          }
1019        }
1020        size_t line_end = i;
1021        if (i < num_characters) {
1022          i++;
1023          if (c == '\r' && i < num_characters && characters[i] == '\n') {
1024            i++;
1025          }
1026        }
1027    
1028        if (characters_start_with(characters, line_start, line_end, "//#JSCOVERAGE_IF")) {
1029          has_conditionals = true;
1030    
1031          if (characters_are_white_space(characters, line_start + 16, line_end)) {
1032            exclusive_directives[line_number - 1] = true;
1033          }
1034          else {
1035            struct IfDirective * if_directive = xnew(struct IfDirective, 1);
1036            if_directive->condition_start = characters + line_start + 16;
1037            if_directive->condition_end = characters + line_end;
1038            if_directive->start_line = line_number;
1039            if_directive->end_line = 0;
1040            if_directive->next = if_directives;
1041            if_directives = if_directive;
1042          }
1043        }
1044        else if (characters_start_with(characters, line_start, line_end, "//#JSCOVERAGE_ENDIF")) {
1045          for (struct IfDirective * p = if_directives; p != NULL; p = p->next) {
1046            if (p->end_line == 0) {
1047              p->end_line = line_number;
1048              break;
1049            }
1050          }
1051        }
1052      }
1053    
1054    /*    /*
1055    An instrumented JavaScript file has 3 sections:    An instrumented JavaScript file has 4 sections:
1056    1. initialization    1. initialization
1057    2. instrumented source code    2. instrumented source code
1058    3. original source code    3. conditionals
1059      4. original source code
1060    */    */
1061    
1062    Stream * instrumented = Stream_new(0);    Stream * instrumented = Stream_new(0);
1063    instrument_statement(node, instrumented, 0);    instrument_statement(node, instrumented, 0, false);
1064    
1065    /* write line number info to the output */    /* write line number info to the output */
1066    Stream_write_string(output, "/* automatically generated by JSCoverage - do not edit */\n");    Stream_write_string(output, "/* automatically generated by JSCoverage - do not edit */\n");
# Line 929  Line 1076 
1076    Stream_write_string(output, "}\n");    Stream_write_string(output, "}\n");
1077    free(lines);    free(lines);
1078    lines = NULL;    lines = NULL;
1079      free(exclusive_directives);
1080      exclusive_directives = NULL;
1081    
1082      /* conditionals */
1083      if (has_conditionals) {
1084        Stream_printf(output, "_$jscoverage['%s'].conditionals = [];\n", file_id);
1085      }
1086    
1087    /* copy the instrumented source code to the output */    /* copy the instrumented source code to the output */
1088    Stream_write(output, instrumented->data, instrumented->length);    Stream_write(output, instrumented->data, instrumented->length);
1089    
1090    /* conditionals */    /* conditionals */
1091    bool has_conditionals = false;    for (struct IfDirective * if_directive = if_directives; if_directive != NULL; if_directive = if_directive->next) {
1092    size_t line_number = 0;      Stream_write_string(output, "if (!(");
1093    size_t i = 0;      for (const jschar * p = if_directive->condition_start; p < if_directive->condition_end; p++) {
1094    while (i < num_characters) {        jschar c = *p;
1095      line_number++;        if (c == '\t' || (32 <= c && c <= 126)) {
1096      size_t line_start = i;          Stream_write_char(output, c);
     /* FIXME */  
     while (i < num_characters && characters[i] != '\r' && characters[i] != '\n') {  
       i++;  
     }  
     size_t line_end = i;  
     if (i < num_characters) {  
       if (characters[i] == '\r') {  
         line_end = i;  
         i++;  
         if (i < num_characters && characters[i] == '\n') {  
           i++;  
         }  
       }  
       else if (characters[i] == '\n') {  
         line_end = i;  
         i++;  
1097        }        }
1098        else {        else {
1099          abort();          Stream_printf(output, "\\u%04x", c);
1100        }        }
1101      }      }
1102      char * line = js_DeflateString(context, characters + line_start, line_end - line_start);      Stream_write_string(output, ")) {\n");
1103      if (str_starts_with(line, "//#JSCOVERAGE_IF")) {      Stream_printf(output, "  _$jscoverage['%s'].conditionals[%d] = %d;\n", file_id, if_directive->start_line, if_directive->end_line);
1104        if (! has_conditionals) {      Stream_write_string(output, "}\n");
1105          has_conditionals = true;    }
1106          Stream_printf(output, "_$jscoverage['%s'].conditionals = [];\n", file_id);  
1107        }    /* free */
1108        Stream_printf(output, "if (!%s) {\n", line + 16);    while (if_directives != NULL) {
1109        Stream_printf(output, "  _$jscoverage['%s'].conditionals[%d] = ", file_id, line_number);      struct IfDirective * if_directive = if_directives;
1110      }      if_directives = if_directives->next;
1111      else if (str_starts_with(line, "//#JSCOVERAGE_ENDIF")) {      free(if_directive);
       Stream_printf(output, "%d;\n", line_number);  
       Stream_printf(output, "}\n");  
     }  
     JS_free(context, line);  
1112    }    }
1113    
1114    /* copy the original source to the output */    /* copy the original source to the output */
# Line 1322  Line 1456 
1456        file_coverage->id = id;        file_coverage->id = id;
1457        file_coverage->num_coverage_lines = array->pn_count;        file_coverage->num_coverage_lines = array->pn_count;
1458        file_coverage->coverage_lines = xnew(int, array->pn_count);        file_coverage->coverage_lines = xnew(int, array->pn_count);
1459        if (source == NULL) {        file_coverage->source_lines = NULL;
         file_coverage->source_lines = NULL;  
       }  
       else {  
         file_coverage->num_source_lines = source->pn_count;  
         file_coverage->source_lines = xnew(char *, source->pn_count);  
         uint32 i = 0;  
         for (JSParseNode * element = source->pn_head; element != NULL; element = element->pn_next, i++) {  
           if (element->pn_type != TOK_STRING) {  
             return -1;  
           }  
           file_coverage->source_lines[i] = xstrdup(JS_GetStringBytes(ATOM_TO_STRING(element->pn_atom)));  
         }  
         assert(i == source->pn_count);  
       }  
1460    
1461        /* set coverage for all lines */        /* set coverage for all lines */
1462        uint32 i = 0;        uint32 i = 0;
# Line 1386  Line 1506 
1506          }          }
1507        }        }
1508        assert(i == array->pn_count);        assert(i == array->pn_count);
1509        }
1510    
1511        /* if this JSON file has source, use it */      /* if this JSON file has source, use it */
1512        if (file_coverage->source_lines == NULL && source != NULL) {      if (file_coverage->source_lines == NULL && source != NULL) {
1513          file_coverage->num_source_lines = source->pn_count;        file_coverage->num_source_lines = source->pn_count;
1514          file_coverage->source_lines = xnew(char *, source->pn_count);        file_coverage->source_lines = xnew(char *, source->pn_count);
1515          uint32 i = 0;        uint32 i = 0;
1516          for (JSParseNode * element = source->pn_head; element != NULL; element = element->pn_next, i++) {        for (JSParseNode * element = source->pn_head; element != NULL; element = element->pn_next, i++) {
1517            if (element->pn_type != TOK_STRING) {          if (element->pn_type != TOK_STRING) {
1518              return -1;            return -1;
           }  
           file_coverage->source_lines[i] = xstrdup(JS_GetStringBytes(ATOM_TO_STRING(element->pn_atom)));  
1519          }          }
1520          assert(i == source->pn_count);          file_coverage->source_lines[i] = xstrdup(JS_GetStringBytes(ATOM_TO_STRING(element->pn_atom)));
1521        }        }
1522          assert(i == source->pn_count);
1523      }      }
1524    }    }
1525    

Legend:
Removed from v.179  
changed lines
  Added in v.245

  ViewVC Help
Powered by ViewVC 1.1.24