/[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 92 by siliconforks, Wed May 7 04:21:22 2008 UTC revision 214 by siliconforks, Fri Oct 3 02:26:04 2008 UTC
# Line 17  Line 17 
17      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  */  */
19    
20    #include <config.h>
21    
22  #include "instrument-js.h"  #include "instrument-js.h"
23    
24  #include <assert.h>  #include <assert.h>
# Line 32  Line 34 
34  #include <jsscope.h>  #include <jsscope.h>
35  #include <jsstr.h>  #include <jsstr.h>
36    
37    #include "encoding.h"
38    #include "global.h"
39    #include "highlight.h"
40    #include "resource-manager.h"
41  #include "util.h"  #include "util.h"
42    
43  static JSRuntime * runtime = NULL;  static JSRuntime * runtime = NULL;
# Line 44  Line 50 
50  */  */
51  static const char * file_id = NULL;  static const char * file_id = NULL;
52  static char * lines = NULL;  static char * lines = NULL;
53    static uint16_t num_lines = 0;
54    
55  void jscoverage_init(void) {  void jscoverage_init(void) {
56    runtime = JS_NewRuntime(8L * 1024L * 1024L);    runtime = JS_NewRuntime(8L * 1024L * 1024L);
# Line 72  Line 79 
79  }  }
80    
81  static void print_string(JSString * s, Stream * f) {  static void print_string(JSString * s, Stream * f) {
82    for (int i = 0; i < s->length; i++) {    size_t length = JSSTRING_LENGTH(s);
83      char c = s->chars[i];    jschar * characters = JSSTRING_CHARS(s);
84      Stream_write_char(f, c);    for (size_t i = 0; i < length; i++) {
85        jschar c = characters[i];
86        if (32 <= c && c <= 126) {
87          switch (c) {
88          case '"':
89            Stream_write_string(f, "\\\"");
90            break;
91    /*
92          case '\'':
93            Stream_write_string(f, "\\'");
94            break;
95    */
96          case '\\':
97            Stream_write_string(f, "\\\\");
98            break;
99          default:
100            Stream_write_char(f, c);
101            break;
102          }
103        }
104        else {
105          switch (c) {
106          case 0x8:
107            Stream_write_string(f, "\\b");
108            break;
109          case 0x9:
110            Stream_write_string(f, "\\t");
111            break;
112          case 0xa:
113            Stream_write_string(f, "\\n");
114            break;
115          case 0xb:
116            Stream_write_string(f, "\\v");
117            break;
118          case 0xc:
119            Stream_write_string(f, "\\f");
120            break;
121          case 0xd:
122            Stream_write_string(f, "\\r");
123            break;
124          default:
125            Stream_printf(f, "\\u%04x", c);
126            break;
127          }
128        }
129    }    }
130  }  }
131    
# Line 84  Line 135 
135    print_string(s, f);    print_string(s, f);
136  }  }
137    
138  static void print_string_jsval(jsval value, Stream * f) {  static void print_regex(jsval value, Stream * f) {
139    assert(JSVAL_IS_STRING(value));    assert(JSVAL_IS_STRING(value));
140    JSString * s = JSVAL_TO_STRING(value);    JSString * s = JSVAL_TO_STRING(value);
141    print_string(s, f);    size_t length = JSSTRING_LENGTH(s);
142      jschar * characters = JSSTRING_CHARS(s);
143      for (size_t i = 0; i < length; i++) {
144        jschar c = characters[i];
145        if (32 <= c && c <= 126) {
146          Stream_write_char(f, c);
147        }
148        else {
149          Stream_printf(f, "\\u%04x", c);
150        }
151      }
152  }  }
153    
154  static void print_quoted_string_atom(JSAtom * atom, Stream * f) {  static void print_quoted_string_atom(JSAtom * atom, Stream * f) {
155    assert(ATOM_IS_STRING(atom));    assert(ATOM_IS_STRING(atom));
156    JSString * s = ATOM_TO_STRING(atom);    JSString * s = ATOM_TO_STRING(atom);
157    JSString * quoted = js_QuoteString(context, s, '"');    Stream_write_char(f, '"');
158    print_string(quoted, f);    print_string(s, f);
159      Stream_write_char(f, '"');
160  }  }
161    
162  static const char * get_op(uint8 op) {  static const char * get_op(uint8 op) {
# Line 145  Line 207 
207  static void instrument_expression(JSParseNode * node, Stream * f);  static void instrument_expression(JSParseNode * node, Stream * f);
208  static void instrument_statement(JSParseNode * node, Stream * f, int indent);  static void instrument_statement(JSParseNode * node, Stream * f, int indent);
209    
210  static void instrument_function(JSParseNode * node, Stream * f, int indent) {  enum FunctionType {
211      assert(node->pn_arity == PN_FUNC);    FUNCTION_NORMAL,
212      assert(ATOM_IS_OBJECT(node->pn_funAtom));    FUNCTION_GETTER_OR_SETTER
213      JSObject * object = ATOM_TO_OBJECT(node->pn_funAtom);  };
214      assert(JS_ObjectIsFunction(context, object));  
215      JSFunction * function = (JSFunction *) JS_GetPrivate(context, object);  static void instrument_function(JSParseNode * node, Stream * f, int indent, enum FunctionType type) {
216      assert(function);    assert(node->pn_arity == PN_FUNC);
217      assert(object == function->object);    assert(ATOM_IS_OBJECT(node->pn_funAtom));
218      Stream_printf(f, "%*s", indent, "");    JSObject * object = ATOM_TO_OBJECT(node->pn_funAtom);
219      assert(JS_ObjectIsFunction(context, object));
220      JSFunction * function = (JSFunction *) JS_GetPrivate(context, object);
221      assert(function);
222      assert(object == function->object);
223      Stream_printf(f, "%*s", indent, "");
224      if (type == FUNCTION_NORMAL) {
225      Stream_write_string(f, "function");      Stream_write_string(f, "function");
226      }
227    
228      /* function name */    /* function name */
229      if (function->atom) {    if (function->atom) {
230        Stream_write_char(f, ' ');      Stream_write_char(f, ' ');
231        print_string_atom(function->atom, f);      print_string_atom(function->atom, f);
232      }    }
233    
234      /* function parameters */    /* function parameters */
235      Stream_write_string(f, "(");    Stream_write_string(f, "(");
236      JSAtom ** params = xmalloc(function->nargs * sizeof(JSAtom *));    JSAtom ** params = xnew(JSAtom *, function->nargs);
237      for (int i = 0; i < function->nargs; i++) {    for (int i = 0; i < function->nargs; i++) {
238        /* initialize to NULL for sanity check */      /* initialize to NULL for sanity check */
239        params[i] = NULL;      params[i] = NULL;
240      }    }
241      JSScope * scope = OBJ_SCOPE(object);    JSScope * scope = OBJ_SCOPE(object);
242      for (JSScopeProperty * scope_property = SCOPE_LAST_PROP(scope); scope_property != NULL; scope_property = scope_property->parent) {    for (JSScopeProperty * scope_property = SCOPE_LAST_PROP(scope); scope_property != NULL; scope_property = scope_property->parent) {
243        if (scope_property->getter != js_GetArgument) {      if (scope_property->getter != js_GetArgument) {
244          continue;        continue;
245        }      }
246        assert(scope_property->flags & SPROP_HAS_SHORTID);      assert(scope_property->flags & SPROP_HAS_SHORTID);
247        assert((uint16) scope_property->shortid < function->nargs);      assert((uint16) scope_property->shortid < function->nargs);
248        assert(JSID_IS_ATOM(scope_property->id));      assert(JSID_IS_ATOM(scope_property->id));
249        params[(uint16) scope_property->shortid] = JSID_TO_ATOM(scope_property->id);      params[(uint16) scope_property->shortid] = JSID_TO_ATOM(scope_property->id);
250      }
251      for (int i = 0; i < function->nargs; i++) {
252        assert(params[i] != NULL);
253        if (i > 0) {
254          Stream_write_string(f, ", ");
255      }      }
256      for (int i = 0; i < function->nargs; i++) {      if (ATOM_IS_STRING(params[i])) {
257        assert(params[i] != NULL);        print_string_atom(params[i], f);
       if (i > 0) {  
         Stream_write_string(f, ", ");  
       }  
       if (ATOM_IS_STRING(params[i])) {  
         print_string_atom(params[i], f);  
       }  
258      }      }
259      Stream_write_string(f, ") {\n");    }
260      free(params);    Stream_write_string(f, ") {\n");
261      free(params);
262    
263      /* function body */    /* function body */
264      instrument_statement(node->pn_body, f, indent + 2);    instrument_statement(node->pn_body, f, indent + 2);
265    
266      Stream_write_string(f, "}\n");    Stream_write_string(f, "}\n");
267  }  }
268    
269  static void instrument_function_call(JSParseNode * node, Stream * f) {  static void instrument_function_call(JSParseNode * node, Stream * f) {
# Line 225  Line 294 
294  static void instrument_expression(JSParseNode * node, Stream * f) {  static void instrument_expression(JSParseNode * node, Stream * f) {
295    switch (node->pn_type) {    switch (node->pn_type) {
296    case TOK_FUNCTION:    case TOK_FUNCTION:
297      instrument_function(node, f, 0);      instrument_function(node, f, 0, FUNCTION_NORMAL);
298      break;      break;
299    case TOK_COMMA:    case TOK_COMMA:
300      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) {
# Line 432  Line 501 
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->pn_left, f);  
505        Stream_write_string(f, ": ");        /* check whether this is a getter or setter */
506        instrument_expression(p->pn_right, f);        switch (p->pn_op) {
507          case JSOP_GETTER:
508            Stream_write_string(f, "get ");
509            instrument_expression(p->pn_left, f);
510            instrument_function(p->pn_right, f, 0, FUNCTION_GETTER_OR_SETTER);
511            break;
512          case JSOP_SETTER:
513            Stream_write_string(f, "set ");
514            instrument_expression(p->pn_left, f);
515            instrument_function(p->pn_right, f, 0, FUNCTION_GETTER_OR_SETTER);
516            break;
517          default:
518            instrument_expression(p->pn_left, f);
519            Stream_write_string(f, ": ");
520            instrument_expression(p->pn_right, f);
521            break;
522          }
523      }      }
524      Stream_write_char(f, '}');      Stream_write_char(f, '}');
525      break;      break;
# Line 461  Line 546 
546          JSObject * object = ATOM_TO_OBJECT(node->pn_atom);          JSObject * object = ATOM_TO_OBJECT(node->pn_atom);
547          jsval result;          jsval result;
548          js_regexp_toString(context, object, 0, NULL, &result);          js_regexp_toString(context, object, 0, NULL, &result);
549          print_string_jsval(result, f);          print_regex(result, f);
550        }        }
551        break;        break;
552      default:      default:
# Line 538  Line 623 
623  static void output_statement(JSParseNode * node, Stream * f, int indent) {  static void output_statement(JSParseNode * node, Stream * f, int indent) {
624    switch (node->pn_type) {    switch (node->pn_type) {
625    case TOK_FUNCTION:    case TOK_FUNCTION:
626      instrument_function(node, f, indent);      instrument_function(node, f, indent, FUNCTION_NORMAL);
627      break;      break;
628    case TOK_LC:    case TOK_LC:
629      assert(node->pn_arity == PN_LIST);      assert(node->pn_arity == PN_LIST);
# Line 790  Line 875 
875  */  */
876  static void instrument_statement(JSParseNode * node, Stream * f, int indent) {  static void instrument_statement(JSParseNode * node, Stream * f, int indent) {
877    if (node->pn_type != TOK_LC) {    if (node->pn_type != TOK_LC) {
878      int line = node->pn_pos.begin.lineno;      uint16_t line = node->pn_pos.begin.lineno;
879        if (line > num_lines) {
880          fatal("%s: script contains more than 65,535 lines", file_id);
881        }
882    
883      /* the root node has line number 0 */      /* the root node has line number 0 */
884      if (line != 0) {      if (line != 0) {
885        Stream_printf(f, "%*s", indent, "");        Stream_printf(f, "%*s", indent, "");
# Line 801  Line 890 
890    output_statement(node, f, indent);    output_statement(node, f, indent);
891  }  }
892    
893  void jscoverage_instrument_js(const char * id, Stream * input, Stream * output) {  static bool characters_start_with(const jschar * characters, size_t line_start, size_t line_end, const char * prefix) {
894      const jschar * characters_end = characters + line_end;
895      const jschar * cp = characters + line_start;
896      const char * bp = prefix;
897      for (;;) {
898        if (*bp == '\0') {
899          return true;
900        }
901        else if (cp == characters_end) {
902          return false;
903        }
904        else if (*cp != *bp) {
905          return false;
906        }
907        bp++;
908        cp++;
909      }
910    }
911    
912    void jscoverage_instrument_js(const char * id, const uint16_t * characters, size_t num_characters, Stream * output) {
913    file_id = id;    file_id = id;
914    
915    /* scan the javascript */    /* scan the javascript */
916    size_t input_length = input->length;    JSTokenStream * token_stream = js_NewTokenStream(context, characters, num_characters, NULL, 1, NULL);
   jschar * base = js_InflateString(context, input->data, &input_length);  
   if (base == NULL) {  
     fatal("out of memory");  
   }  
   JSTokenStream * token_stream = js_NewTokenStream(context, base, input_length, NULL, 1, NULL);  
917    if (token_stream == NULL) {    if (token_stream == NULL) {
918      fatal("cannot create token stream from file: %s", file_id);      fatal("cannot create token stream from file: %s", file_id);
919    }    }
# Line 820  Line 923 
923    if (node == NULL) {    if (node == NULL) {
924      fatal("parse error in file: %s", file_id);      fatal("parse error in file: %s", file_id);
925    }    }
926    int num_lines = node->pn_pos.end.lineno;    num_lines = node->pn_pos.end.lineno;
927    lines = xmalloc(num_lines);    lines = xmalloc(num_lines);
928    for (int i = 0; i < num_lines; i++) {    for (int i = 0; i < num_lines; i++) {
929      lines[i] = 0;      lines[i] = 0;
# Line 830  Line 933 
933    An instrumented JavaScript file has 3 sections:    An instrumented JavaScript file has 3 sections:
934    1. initialization    1. initialization
935    2. instrumented source code    2. instrumented source code
936    3. original source code (TODO)    3. original source code
937    */    */
938    
939    Stream * instrumented = Stream_new(0);    Stream * instrumented = Stream_new(0);
# Line 854  Line 957 
957    /* copy the instrumented source code to the output */    /* copy the instrumented source code to the output */
958    Stream_write(output, instrumented->data, instrumented->length);    Stream_write(output, instrumented->data, instrumented->length);
959    
960      /* conditionals */
961      bool has_conditionals = false;
962      size_t line_number = 0;
963      size_t i = 0;
964      while (i < num_characters) {
965        line_number++;
966        size_t line_start = i;
967        jschar c;
968        bool done = false;
969        while (! done && i < num_characters) {
970          c = characters[i];
971          switch (c) {
972          case '\r':
973          case '\n':
974          case 0x2028:
975          case 0x2029:
976            done = true;
977            break;
978          default:
979            i++;
980            break;
981          }
982        }
983        size_t line_end = i;
984        if (i < num_characters) {
985          i++;
986          if (c == '\r' && i < num_characters && characters[i] == '\n') {
987            i++;
988          }
989        }
990        if (characters_start_with(characters, line_start, line_end, "//#JSCOVERAGE_IF")) {
991          if (! has_conditionals) {
992            has_conditionals = true;
993            Stream_printf(output, "_$jscoverage['%s'].conditionals = [];\n", file_id);
994          }
995          Stream_write_string(output, "if (!(");
996          for (size_t j = line_start + 16; j < line_end; j++) {
997            jschar c = characters[j];
998            if (c == '\t' || (32 <= c && c <= 126)) {
999              Stream_write_char(output, c);
1000            }
1001            else {
1002              Stream_printf(output, "\\u%04x", c);
1003            }
1004          }
1005          Stream_write_string(output, ")) {\n");
1006          Stream_printf(output, "  _$jscoverage['%s'].conditionals[%d] = ", file_id, line_number);
1007        }
1008        else if (characters_start_with(characters, line_start, line_end, "//#JSCOVERAGE_ENDIF")) {
1009          Stream_printf(output, "%d;\n", line_number);
1010          Stream_printf(output, "}\n");
1011        }
1012      }
1013    
1014      /* copy the original source to the output */
1015      Stream_printf(output, "_$jscoverage['%s'].source = ", file_id);
1016      jscoverage_write_source(id, characters, num_characters, output);
1017      Stream_printf(output, ";\n");
1018    
1019    Stream_delete(instrumented);    Stream_delete(instrumented);
1020    
1021      file_id = NULL;
1022    }
1023    
1024    void jscoverage_write_source(const char * id, const jschar * characters, size_t num_characters, Stream * output) {
1025      Stream_write_string(output, "[");
1026      if (jscoverage_highlight) {
1027        Stream * highlighted_stream = Stream_new(num_characters);
1028        jscoverage_highlight_js(context, id, characters, num_characters, highlighted_stream);
1029        size_t i = 0;
1030        while (i < highlighted_stream->length) {
1031          if (i > 0) {
1032            Stream_write_char(output, ',');
1033          }
1034    
1035          Stream_write_char(output, '"');
1036          bool done = false;
1037          while (! done) {
1038            char c = highlighted_stream->data[i];
1039            switch (c) {
1040            case 0x8:
1041              /* backspace */
1042              Stream_write_string(output, "\\b");
1043              break;
1044            case 0x9:
1045              /* horizontal tab */
1046              Stream_write_string(output, "\\t");
1047              break;
1048            case 0xa:
1049              /* line feed (new line) */
1050              done = true;
1051              break;
1052            case 0xb:
1053              /* vertical tab */
1054              Stream_write_string(output, "\\v");
1055              break;
1056            case 0xc:
1057              /* form feed */
1058              Stream_write_string(output, "\\f");
1059              break;
1060            case 0xd:
1061              /* carriage return */
1062              done = true;
1063              if (i + 1 < highlighted_stream->length && highlighted_stream->data[i + 1] == '\n') {
1064                i++;
1065              }
1066              break;
1067            case '"':
1068              Stream_write_string(output, "\\\"");
1069              break;
1070            case '\\':
1071              Stream_write_string(output, "\\\\");
1072              break;
1073            default:
1074              Stream_write_char(output, c);
1075              break;
1076            }
1077            i++;
1078            if (i >= highlighted_stream->length) {
1079              done = true;
1080            }
1081          }
1082          Stream_write_char(output, '"');
1083        }
1084        Stream_delete(highlighted_stream);
1085      }
1086      else {
1087        size_t i = 0;
1088        while (i < num_characters) {
1089          if (i > 0) {
1090            Stream_write_char(output, ',');
1091          }
1092    
1093          Stream_write_char(output, '"');
1094          bool done = false;
1095          while (! done) {
1096            jschar c = characters[i];
1097            switch (c) {
1098            case 0x8:
1099              /* backspace */
1100              Stream_write_string(output, "\\b");
1101              break;
1102            case 0x9:
1103              /* horizontal tab */
1104              Stream_write_string(output, "\\t");
1105              break;
1106            case 0xa:
1107              /* line feed (new line) */
1108              done = true;
1109              break;
1110            case 0xb:
1111              /* vertical tab */
1112              Stream_write_string(output, "\\v");
1113              break;
1114            case 0xc:
1115              /* form feed */
1116              Stream_write_string(output, "\\f");
1117              break;
1118            case 0xd:
1119              /* carriage return */
1120              done = true;
1121              if (i + 1 < num_characters && characters[i + 1] == '\n') {
1122                i++;
1123              }
1124              break;
1125            case '"':
1126              Stream_write_string(output, "\\\"");
1127              break;
1128            case '\\':
1129              Stream_write_string(output, "\\\\");
1130              break;
1131            case '&':
1132              Stream_write_string(output, "&amp;");
1133              break;
1134            case '<':
1135              Stream_write_string(output, "&lt;");
1136              break;
1137            case '>':
1138              Stream_write_string(output, "&gt;");
1139              break;
1140            case 0x2028:
1141            case 0x2029:
1142              done = true;
1143              break;
1144            default:
1145              if (32 <= c && c <= 126) {
1146                Stream_write_char(output, c);
1147              }
1148              else {
1149                Stream_printf(output, "&#%d;", c);
1150              }
1151              break;
1152            }
1153            i++;
1154            if (i >= num_characters) {
1155              done = true;
1156            }
1157          }
1158          Stream_write_char(output, '"');
1159        }
1160      }
1161      Stream_write_string(output, "]");
1162    }
1163    
1164    void jscoverage_copy_resources(const char * destination_directory) {
1165      copy_resource("jscoverage.html", destination_directory);
1166      copy_resource("jscoverage.css", destination_directory);
1167      copy_resource("jscoverage.js", destination_directory);
1168      copy_resource("jscoverage-ie.css", destination_directory);
1169      copy_resource("jscoverage-throbber.gif", destination_directory);
1170      copy_resource("jscoverage-highlight.css", destination_directory);
1171    }
1172    
1173    /*
1174    coverage reports
1175    */
1176    
1177    struct FileCoverageList {
1178      FileCoverage * file_coverage;
1179      struct FileCoverageList * next;
1180    };
1181    
1182    struct Coverage {
1183      JSHashTable * coverage_table;
1184      struct FileCoverageList * coverage_list;
1185    };
1186    
1187    static int compare_strings(const void * p1, const void * p2) {
1188      return strcmp(p1, p2) == 0;
1189    }
1190    
1191    Coverage * Coverage_new(void) {
1192      Coverage * result = xmalloc(sizeof(Coverage));
1193      result->coverage_table = JS_NewHashTable(1024, JS_HashString, compare_strings, NULL, NULL, NULL);
1194      if (result->coverage_table == NULL) {
1195        fatal("cannot create hash table");
1196      }
1197      result->coverage_list = NULL;
1198      return result;
1199    }
1200    
1201    void Coverage_delete(Coverage * coverage) {
1202      JS_HashTableDestroy(coverage->coverage_table);
1203      struct FileCoverageList * p = coverage->coverage_list;
1204      while (p != NULL) {
1205        free(p->file_coverage->coverage_lines);
1206        if (p->file_coverage->source_lines != NULL) {
1207          for (uint32 i = 0; i < p->file_coverage->num_source_lines; i++) {
1208            free(p->file_coverage->source_lines[i]);
1209          }
1210          free(p->file_coverage->source_lines);
1211        }
1212        free(p->file_coverage->id);
1213        free(p->file_coverage);
1214        struct FileCoverageList * q = p;
1215        p = p->next;
1216        free(q);
1217      }
1218      free(coverage);
1219    }
1220    
1221    struct EnumeratorArg {
1222      CoverageForeachFunction f;
1223      void * p;
1224    };
1225    
1226    static intN enumerator(JSHashEntry * entry, intN i, void * arg) {
1227      struct EnumeratorArg * enumerator_arg = arg;
1228      enumerator_arg->f(entry->value, i, enumerator_arg->p);
1229      return 0;
1230    }
1231    
1232    void Coverage_foreach_file(Coverage * coverage, CoverageForeachFunction f, void * p) {
1233      struct EnumeratorArg enumerator_arg;
1234      enumerator_arg.f = f;
1235      enumerator_arg.p = p;
1236      JS_HashTableEnumerateEntries(coverage->coverage_table, enumerator, &enumerator_arg);
1237    }
1238    
1239    int jscoverage_parse_json(Coverage * coverage, const uint8_t * json, size_t length) {
1240      jschar * base = js_InflateString(context, (char *) json, &length);
1241      if (base == NULL) {
1242        fatal("out of memory");
1243      }
1244    
1245      jschar * parenthesized_json = xnew(jschar, addst(length, 2));
1246      parenthesized_json[0] = '(';
1247      memcpy(parenthesized_json + 1, base, mulst(length, sizeof(jschar)));
1248      parenthesized_json[length + 1] = ')';
1249    
1250    JS_free(context, base);    JS_free(context, base);
1251    
1252    file_id = NULL;    JSTokenStream * token_stream = js_NewTokenStream(context, parenthesized_json, length + 2, NULL, 1, NULL);
1253      if (token_stream == NULL) {
1254        fatal("cannot create token stream");
1255      }
1256    
1257      JSParseNode * root = js_ParseTokenStream(context, global, token_stream);
1258      free(parenthesized_json);
1259      if (root == NULL) {
1260        return -1;
1261      }
1262    
1263      /* root node must be TOK_LC */
1264      if (root->pn_type != TOK_LC) {
1265        return -1;
1266      }
1267      JSParseNode * semi = root->pn_u.list.head;
1268    
1269      /* the list must be TOK_SEMI and it must contain only one element */
1270      if (semi->pn_type != TOK_SEMI || semi->pn_next != NULL) {
1271        return -1;
1272      }
1273      JSParseNode * parenthesized = semi->pn_kid;
1274    
1275      /* this must be a parenthesized expression */
1276      if (parenthesized->pn_type != TOK_RP) {
1277        return -1;
1278      }
1279      JSParseNode * object = parenthesized->pn_kid;
1280    
1281      /* this must be an object literal */
1282      if (object->pn_type != TOK_RC) {
1283        return -1;
1284      }
1285    
1286      for (JSParseNode * p = object->pn_head; p != NULL; p = p->pn_next) {
1287        /* every element of this list must be TOK_COLON */
1288        if (p->pn_type != TOK_COLON) {
1289          return -1;
1290        }
1291    
1292        /* the key must be a string representing the file */
1293        JSParseNode * key = p->pn_left;
1294        if (key->pn_type != TOK_STRING || ! ATOM_IS_STRING(key->pn_atom)) {
1295          return -1;
1296        }
1297        char * id_bytes = JS_GetStringBytes(ATOM_TO_STRING(key->pn_atom));
1298    
1299        /* the value must be an object literal OR an array */
1300        JSParseNode * value = p->pn_right;
1301        if (! (value->pn_type == TOK_RC || value->pn_type == TOK_RB)) {
1302          return -1;
1303        }
1304    
1305        JSParseNode * array = NULL;
1306        JSParseNode * source = NULL;
1307        if (value->pn_type == TOK_RB) {
1308          /* an array */
1309          array = value;
1310        }
1311        else if (value->pn_type == TOK_RC) {
1312          /* an object literal */
1313          if (value->pn_count != 2) {
1314            return -1;
1315          }
1316          for (JSParseNode * element = value->pn_head; element != NULL; element = element->pn_next) {
1317            if (element->pn_type != TOK_COLON) {
1318              return -1;
1319            }
1320            JSParseNode * left = element->pn_left;
1321            if (left->pn_type != TOK_STRING || ! ATOM_IS_STRING(left->pn_atom)) {
1322              return -1;
1323            }
1324            const char * s = JS_GetStringBytes(ATOM_TO_STRING(left->pn_atom));
1325            if (strcmp(s, "coverage") == 0) {
1326              array = element->pn_right;
1327              if (array->pn_type != TOK_RB) {
1328                return -1;
1329              }
1330            }
1331            else if (strcmp(s, "source") == 0) {
1332              source = element->pn_right;
1333              if (source->pn_type != TOK_RB) {
1334                return -1;
1335              }
1336            }
1337            else {
1338              return -1;
1339            }
1340          }
1341        }
1342        else {
1343          return -1;
1344        }
1345    
1346        if (array == NULL) {
1347          return -1;
1348        }
1349    
1350        /* look up the file in the coverage table */
1351        FileCoverage * file_coverage = JS_HashTableLookup(coverage->coverage_table, id_bytes);
1352        if (file_coverage == NULL) {
1353          /* not there: create a new one */
1354          char * id = xstrdup(id_bytes);
1355          file_coverage = xmalloc(sizeof(FileCoverage));
1356          file_coverage->id = id;
1357          file_coverage->num_coverage_lines = array->pn_count;
1358          file_coverage->coverage_lines = xnew(int, array->pn_count);
1359          if (source == NULL) {
1360            file_coverage->source_lines = NULL;
1361          }
1362          else {
1363            file_coverage->num_source_lines = source->pn_count;
1364            file_coverage->source_lines = xnew(char *, source->pn_count);
1365            uint32 i = 0;
1366            for (JSParseNode * element = source->pn_head; element != NULL; element = element->pn_next, i++) {
1367              if (element->pn_type != TOK_STRING) {
1368                return -1;
1369              }
1370              file_coverage->source_lines[i] = xstrdup(JS_GetStringBytes(ATOM_TO_STRING(element->pn_atom)));
1371            }
1372            assert(i == source->pn_count);
1373          }
1374    
1375          /* set coverage for all lines */
1376          uint32 i = 0;
1377          for (JSParseNode * element = array->pn_head; element != NULL; element = element->pn_next, i++) {
1378            if (element->pn_type == TOK_NUMBER) {
1379              file_coverage->coverage_lines[i] = (int) element->pn_dval;
1380            }
1381            else if (element->pn_type == TOK_PRIMARY && element->pn_op == JSOP_NULL) {
1382              file_coverage->coverage_lines[i] = -1;
1383            }
1384            else {
1385              return -1;
1386            }
1387          }
1388          assert(i == array->pn_count);
1389    
1390          /* add to the hash table */
1391          JS_HashTableAdd(coverage->coverage_table, id, file_coverage);
1392          struct FileCoverageList * coverage_list = xmalloc(sizeof(struct FileCoverageList));
1393          coverage_list->file_coverage = file_coverage;
1394          coverage_list->next = coverage->coverage_list;
1395          coverage->coverage_list = coverage_list;
1396        }
1397        else {
1398          /* sanity check */
1399          assert(strcmp(file_coverage->id, id_bytes) == 0);
1400          if (file_coverage->num_coverage_lines != array->pn_count) {
1401            return -2;
1402          }
1403    
1404          /* merge the coverage */
1405          uint32 i = 0;
1406          for (JSParseNode * element = array->pn_head; element != NULL; element = element->pn_next, i++) {
1407            if (element->pn_type == TOK_NUMBER) {
1408              if (file_coverage->coverage_lines[i] == -1) {
1409                return -2;
1410              }
1411              file_coverage->coverage_lines[i] += (int) element->pn_dval;
1412            }
1413            else if (element->pn_type == TOK_PRIMARY && element->pn_op == JSOP_NULL) {
1414              if (file_coverage->coverage_lines[i] != -1) {
1415                return -2;
1416              }
1417            }
1418            else {
1419              return -1;
1420            }
1421          }
1422          assert(i == array->pn_count);
1423    
1424          /* if this JSON file has source, use it */
1425          if (file_coverage->source_lines == NULL && source != NULL) {
1426            file_coverage->num_source_lines = source->pn_count;
1427            file_coverage->source_lines = xnew(char *, source->pn_count);
1428            uint32 i = 0;
1429            for (JSParseNode * element = source->pn_head; element != NULL; element = element->pn_next, i++) {
1430              if (element->pn_type != TOK_STRING) {
1431                return -1;
1432              }
1433              file_coverage->source_lines[i] = xstrdup(JS_GetStringBytes(ATOM_TO_STRING(element->pn_atom)));
1434            }
1435            assert(i == source->pn_count);
1436          }
1437        }
1438      }
1439    
1440      return 0;
1441  }  }

Legend:
Removed from v.92  
changed lines
  Added in v.214

  ViewVC Help
Powered by ViewVC 1.1.24