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

Diff of /trunk/instrument-js.cpp

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

revision 239 by siliconforks, Fri Oct 3 02:26:04 2008 UTC revision 240 by siliconforks, Fri Oct 3 23:51:32 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 205  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 261  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 620  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 631  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 639  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 676  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 691  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 761  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 774  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 789  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 797  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 823  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 861  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 873  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      uint16_t line = node->pn_pos.begin.lineno;      uint16_t line = node->pn_pos.begin.lineno;
921      if (line > num_lines) {      if (line > num_lines) {
# Line 887  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) {  static bool characters_start_with(const jschar * characters, size_t line_start, size_t line_end, const char * prefix) {
# Line 909  Line 951 
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) {
970    file_id = id;    file_id = id;
971    
# Line 925  Line 982 
982    }    }
983    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    An instrumented JavaScript file has 3 sections:    exclusive_directives = xnew(bool, num_lines);
991    1. initialization    for (unsigned int i = 0; i < num_lines; i++) {
992    2. instrumented source code      exclusive_directives[i] = false;
   3. original source code  
   */  
   
   Stream * instrumented = Stream_new(0);  
   instrument_statement(node, instrumented, 0);  
   
   /* write line number info to the output */  
   Stream_write_string(output, "/* automatically generated by JSCoverage - do not edit */\n");  
   Stream_write_string(output, "if (! top._$jscoverage) {\n  top._$jscoverage = {};\n}\n");  
   Stream_write_string(output, "var _$jscoverage = top._$jscoverage;\n");  
   Stream_printf(output, "if (! _$jscoverage['%s']) {\n", file_id);  
   Stream_printf(output, "  _$jscoverage['%s'] = [];\n", file_id);  
   for (int i = 0; i < num_lines; i++) {  
     if (lines[i]) {  
       Stream_printf(output, "  _$jscoverage['%s'][%d] = 0;\n", file_id, i + 1);  
     }  
993    }    }
   Stream_write_string(output, "}\n");  
   free(lines);  
   lines = NULL;  
   
   /* copy the instrumented source code to the output */  
   Stream_write(output, instrumented->data, instrumented->length);  
994    
   /* conditionals */  
995    bool has_conditionals = false;    bool has_conditionals = false;
996      struct IfDirective * if_directives = NULL;
997    size_t line_number = 0;    size_t line_number = 0;
998    size_t i = 0;    size_t i = 0;
999    while (i < num_characters) {    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++;      line_number++;
1004      size_t line_start = i;      size_t line_start = i;
1005      jschar c;      jschar c;
# Line 977  Line 1015 
1015          break;          break;
1016        default:        default:
1017          i++;          i++;
         break;  
1018        }        }
1019      }      }
1020      size_t line_end = i;      size_t line_end = i;
# Line 987  Line 1024 
1024          i++;          i++;
1025        }        }
1026      }      }
1027    
1028      if (characters_start_with(characters, line_start, line_end, "//#JSCOVERAGE_IF")) {      if (characters_start_with(characters, line_start, line_end, "//#JSCOVERAGE_IF")) {
1029        if (! has_conditionals) {        has_conditionals = true;
1030          has_conditionals = true;  
1031          Stream_printf(output, "_$jscoverage['%s'].conditionals = [];\n", file_id);        if (characters_are_white_space(characters, line_start + 16, line_end)) {
1032        }          exclusive_directives[line_number - 1] = true;
1033        Stream_write_string(output, "if (!(");        }
1034        for (size_t j = line_start + 16; j < line_end; j++) {        else {
1035          jschar c = characters[j];          struct IfDirective * if_directive = xnew(struct IfDirective, 1);
1036          if (c == '\t' || (32 <= c && c <= 126)) {          if_directive->condition_start = characters + line_start + 16;
1037            Stream_write_char(output, c);          if_directive->condition_end = characters + line_end;
1038          }          if_directive->start_line = line_number;
1039          else {          if_directive->end_line = 0;
1040            Stream_printf(output, "\\u%04x", c);          if_directive->next = if_directives;
1041          }          if_directives = if_directive;
1042        }        }
       Stream_write_string(output, ")) {\n");  
       Stream_printf(output, "  _$jscoverage['%s'].conditionals[%d] = ", file_id, line_number);  
1043      }      }
1044      else if (characters_start_with(characters, line_start, line_end, "//#JSCOVERAGE_ENDIF")) {      else if (characters_start_with(characters, line_start, line_end, "//#JSCOVERAGE_ENDIF")) {
1045        Stream_printf(output, "%d;\n", line_number);        for (struct IfDirective * p = if_directives; p != NULL; p = p->next) {
1046        Stream_printf(output, "}\n");          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 4 sections:
1056      1. initialization
1057      2. instrumented source code
1058      3. conditionals
1059      4. original source code
1060      */
1061    
1062      Stream * instrumented = Stream_new(0);
1063      instrument_statement(node, instrumented, 0, false);
1064    
1065      /* write line number info to the output */
1066      Stream_write_string(output, "/* automatically generated by JSCoverage - do not edit */\n");
1067      Stream_write_string(output, "if (! top._$jscoverage) {\n  top._$jscoverage = {};\n}\n");
1068      Stream_write_string(output, "var _$jscoverage = top._$jscoverage;\n");
1069      Stream_printf(output, "if (! _$jscoverage['%s']) {\n", file_id);
1070      Stream_printf(output, "  _$jscoverage['%s'] = [];\n", file_id);
1071      for (int i = 0; i < num_lines; i++) {
1072        if (lines[i]) {
1073          Stream_printf(output, "  _$jscoverage['%s'][%d] = 0;\n", file_id, i + 1);
1074      }      }
1075    }    }
1076      Stream_write_string(output, "}\n");
1077      free(lines);
1078      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 */
1088      Stream_write(output, instrumented->data, instrumented->length);
1089    
1090      /* conditionals */
1091      for (struct IfDirective * if_directive = if_directives; if_directive != NULL; if_directive = if_directive->next) {
1092        Stream_write_string(output, "if (!(");
1093        for (const jschar * p = if_directive->condition_start; p < if_directive->condition_end; p++) {
1094          jschar c = *p;
1095          if (c == '\t' || (32 <= c && c <= 126)) {
1096            Stream_write_char(output, c);
1097          }
1098          else {
1099            Stream_printf(output, "\\u%04x", c);
1100          }
1101        }
1102        Stream_write_string(output, ")) {\n");
1103        Stream_printf(output, "  _$jscoverage['%s'].conditionals[%d] = %d;\n", file_id, if_directive->start_line, if_directive->end_line);
1104        Stream_write_string(output, "}\n");
1105      }
1106    
1107      /* free */
1108      while (if_directives != NULL) {
1109        struct IfDirective * if_directive = if_directives;
1110        if_directives = if_directives->next;
1111        free(if_directive);
1112      }
1113    
1114    /* copy the original source to the output */    /* copy the original source to the output */
1115    Stream_printf(output, "_$jscoverage['%s'].source = ", file_id);    Stream_printf(output, "_$jscoverage['%s'].source = ", file_id);

Legend:
Removed from v.239  
changed lines
  Added in v.240

  ViewVC Help
Powered by ViewVC 1.1.24