/[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 95 by siliconforks, Wed May 7 22:50:00 2008 UTC revision 176 by siliconforks, Sat Sep 20 23:29:42 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 "resource-manager.h"
39  #include "util.h"  #include "util.h"
40    
41  static JSRuntime * runtime = NULL;  static JSRuntime * runtime = NULL;
# Line 72  Line 76 
76  }  }
77    
78  static void print_string(JSString * s, Stream * f) {  static void print_string(JSString * s, Stream * f) {
79    for (int i = 0; i < s->length; i++) {    size_t length = JSSTRING_LENGTH(s);
80      char c = s->chars[i];    jschar * characters = JSSTRING_CHARS(s);
81      Stream_write_char(f, c);    for (size_t i = 0; i < length; i++) {
82        jschar c = characters[i];
83        if (32 <= c && c <= 126) {
84          switch (c) {
85          case '"':
86            Stream_write_string(f, "\\\"");
87            break;
88    /*
89          case '\'':
90            Stream_write_string(f, "\\'");
91            break;
92    */
93          case '\\':
94            Stream_write_string(f, "\\\\");
95            break;
96          default:
97            Stream_write_char(f, c);
98            break;
99          }
100        }
101        else {
102          switch (c) {
103          case 0x8:
104            Stream_write_string(f, "\\b");
105            break;
106          case 0x9:
107            Stream_write_string(f, "\\t");
108            break;
109          case 0xa:
110            Stream_write_string(f, "\\n");
111            break;
112          case 0xb:
113            Stream_write_string(f, "\\v");
114            break;
115          case 0xc:
116            Stream_write_string(f, "\\f");
117            break;
118          case 0xd:
119            Stream_write_string(f, "\\r");
120            break;
121          default:
122            Stream_printf(f, "\\u%04x", c);
123            break;
124          }
125        }
126    }    }
127  }  }
128    
# Line 84  Line 132 
132    print_string(s, f);    print_string(s, f);
133  }  }
134    
135  static void print_string_jsval(jsval value, Stream * f) {  static void print_regex(jsval value, Stream * f) {
136    assert(JSVAL_IS_STRING(value));    assert(JSVAL_IS_STRING(value));
137    JSString * s = JSVAL_TO_STRING(value);    JSString * s = JSVAL_TO_STRING(value);
138    print_string(s, f);    size_t length = JSSTRING_LENGTH(s);
139      jschar * characters = JSSTRING_CHARS(s);
140      for (size_t i = 0; i < length; i++) {
141        jschar c = characters[i];
142        if (32 <= c && c <= 126) {
143          Stream_write_char(f, c);
144        }
145        else {
146          Stream_printf(f, "\\u%04x", c);
147        }
148      }
149  }  }
150    
151  static void print_quoted_string_atom(JSAtom * atom, Stream * f) {  static void print_quoted_string_atom(JSAtom * atom, Stream * f) {
152    assert(ATOM_IS_STRING(atom));    assert(ATOM_IS_STRING(atom));
153    JSString * s = ATOM_TO_STRING(atom);    JSString * s = ATOM_TO_STRING(atom);
154    JSString * quoted = js_QuoteString(context, s, '"');    Stream_write_char(f, '"');
155    print_string(quoted, f);    print_string(s, f);
156      Stream_write_char(f, '"');
157  }  }
158    
159  static const char * get_op(uint8 op) {  static const char * get_op(uint8 op) {
# Line 145  Line 204 
204  static void instrument_expression(JSParseNode * node, Stream * f);  static void instrument_expression(JSParseNode * node, Stream * f);
205  static void instrument_statement(JSParseNode * node, Stream * f, int indent);  static void instrument_statement(JSParseNode * node, Stream * f, int indent);
206    
207  static void instrument_function(JSParseNode * node, Stream * f, int indent) {  enum FunctionType {
208      assert(node->pn_arity == PN_FUNC);    FUNCTION_NORMAL,
209      assert(ATOM_IS_OBJECT(node->pn_funAtom));    FUNCTION_GETTER_OR_SETTER
210      JSObject * object = ATOM_TO_OBJECT(node->pn_funAtom);  };
211      assert(JS_ObjectIsFunction(context, object));  
212      JSFunction * function = (JSFunction *) JS_GetPrivate(context, object);  static void instrument_function(JSParseNode * node, Stream * f, int indent, enum FunctionType type) {
213      assert(function);    assert(node->pn_arity == PN_FUNC);
214      assert(object == function->object);    assert(ATOM_IS_OBJECT(node->pn_funAtom));
215      Stream_printf(f, "%*s", indent, "");    JSObject * object = ATOM_TO_OBJECT(node->pn_funAtom);
216      assert(JS_ObjectIsFunction(context, object));
217      JSFunction * function = (JSFunction *) JS_GetPrivate(context, object);
218      assert(function);
219      assert(object == function->object);
220      Stream_printf(f, "%*s", indent, "");
221      if (type == FUNCTION_NORMAL) {
222      Stream_write_string(f, "function");      Stream_write_string(f, "function");
223      }
224    
225      /* function name */    /* function name */
226      if (function->atom) {    if (function->atom) {
227        Stream_write_char(f, ' ');      Stream_write_char(f, ' ');
228        print_string_atom(function->atom, f);      print_string_atom(function->atom, f);
229      }    }
230    
231      /* function parameters */    /* function parameters */
232      Stream_write_string(f, "(");    Stream_write_string(f, "(");
233      JSAtom ** params = xmalloc(function->nargs * sizeof(JSAtom *));    JSAtom ** params = xnew(JSAtom *, function->nargs);
234      for (int i = 0; i < function->nargs; i++) {    for (int i = 0; i < function->nargs; i++) {
235        /* initialize to NULL for sanity check */      /* initialize to NULL for sanity check */
236        params[i] = NULL;      params[i] = NULL;
237      }    }
238      JSScope * scope = OBJ_SCOPE(object);    JSScope * scope = OBJ_SCOPE(object);
239      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) {
240        if (scope_property->getter != js_GetArgument) {      if (scope_property->getter != js_GetArgument) {
241          continue;        continue;
242        }      }
243        assert(scope_property->flags & SPROP_HAS_SHORTID);      assert(scope_property->flags & SPROP_HAS_SHORTID);
244        assert((uint16) scope_property->shortid < function->nargs);      assert((uint16) scope_property->shortid < function->nargs);
245        assert(JSID_IS_ATOM(scope_property->id));      assert(JSID_IS_ATOM(scope_property->id));
246        params[(uint16) scope_property->shortid] = JSID_TO_ATOM(scope_property->id);      params[(uint16) scope_property->shortid] = JSID_TO_ATOM(scope_property->id);
247      }    }
248      for (int i = 0; i < function->nargs; i++) {    for (int i = 0; i < function->nargs; i++) {
249        assert(params[i] != NULL);      assert(params[i] != NULL);
250        if (i > 0) {      if (i > 0) {
251          Stream_write_string(f, ", ");        Stream_write_string(f, ", ");
       }  
       if (ATOM_IS_STRING(params[i])) {  
         print_string_atom(params[i], f);  
       }  
252      }      }
253      Stream_write_string(f, ") {\n");      if (ATOM_IS_STRING(params[i])) {
254      free(params);        print_string_atom(params[i], f);
255        }
256      }
257      Stream_write_string(f, ") {\n");
258      free(params);
259    
260      /* function body */    /* function body */
261      instrument_statement(node->pn_body, f, indent + 2);    instrument_statement(node->pn_body, f, indent + 2);
262    
263      Stream_write_string(f, "}\n");    Stream_write_string(f, "}\n");
264  }  }
265    
266  static void instrument_function_call(JSParseNode * node, Stream * f) {  static void instrument_function_call(JSParseNode * node, Stream * f) {
# Line 225  Line 291 
291  static void instrument_expression(JSParseNode * node, Stream * f) {  static void instrument_expression(JSParseNode * node, Stream * f) {
292    switch (node->pn_type) {    switch (node->pn_type) {
293    case TOK_FUNCTION:    case TOK_FUNCTION:
294      instrument_function(node, f, 0);      instrument_function(node, f, 0, FUNCTION_NORMAL);
295      break;      break;
296    case TOK_COMMA:    case TOK_COMMA:
297      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 498 
498        if (p != node->pn_head) {        if (p != node->pn_head) {
499          Stream_write_string(f, ", ");          Stream_write_string(f, ", ");
500        }        }
501        instrument_expression(p->pn_left, f);  
502        Stream_write_string(f, ": ");        /* check whether this is a getter or setter */
503        instrument_expression(p->pn_right, f);        switch (p->pn_op) {
504          case JSOP_GETTER:
505            Stream_write_string(f, "get ");
506            instrument_expression(p->pn_left, f);
507            instrument_function(p->pn_right, f, 0, FUNCTION_GETTER_OR_SETTER);
508            break;
509          case JSOP_SETTER:
510            Stream_write_string(f, "set ");
511            instrument_expression(p->pn_left, f);
512            instrument_function(p->pn_right, f, 0, FUNCTION_GETTER_OR_SETTER);
513            break;
514          default:
515            instrument_expression(p->pn_left, f);
516            Stream_write_string(f, ": ");
517            instrument_expression(p->pn_right, f);
518            break;
519          }
520      }      }
521      Stream_write_char(f, '}');      Stream_write_char(f, '}');
522      break;      break;
# Line 461  Line 543 
543          JSObject * object = ATOM_TO_OBJECT(node->pn_atom);          JSObject * object = ATOM_TO_OBJECT(node->pn_atom);
544          jsval result;          jsval result;
545          js_regexp_toString(context, object, 0, NULL, &result);          js_regexp_toString(context, object, 0, NULL, &result);
546          print_string_jsval(result, f);          print_regex(result, f);
547        }        }
548        break;        break;
549      default:      default:
# Line 538  Line 620 
620  static void output_statement(JSParseNode * node, Stream * f, int indent) {  static void output_statement(JSParseNode * node, Stream * f, int indent) {
621    switch (node->pn_type) {    switch (node->pn_type) {
622    case TOK_FUNCTION:    case TOK_FUNCTION:
623      instrument_function(node, f, indent);      instrument_function(node, f, indent, FUNCTION_NORMAL);
624      break;      break;
625    case TOK_LC:    case TOK_LC:
626      assert(node->pn_arity == PN_LIST);      assert(node->pn_arity == PN_LIST);
# Line 801  Line 883 
883    output_statement(node, f, indent);    output_statement(node, f, indent);
884  }  }
885    
886  void jscoverage_instrument_js(const char * id, Stream * input, Stream * output) {  void jscoverage_instrument_js(const char * id, const char * encoding, Stream * input, Stream * output) {
887    file_id = id;    file_id = id;
888    
889    /* scan the javascript */    /* scan the javascript */
890    size_t input_length = input->length;    size_t num_characters = input->length;
891    jschar * base = js_InflateString(context, input->data, &input_length);    jschar * base = NULL;
892    if (base == NULL) {    int result = jscoverage_bytes_to_characters(encoding, input->data, input->length, &base, &num_characters);
893      fatal("out of memory");    if (result == JSCOVERAGE_ERROR_ENCODING_NOT_SUPPORTED) {
894        fatal("encoding %s not supported in file %s", encoding, id);
895      }
896      else if (result == JSCOVERAGE_ERROR_INVALID_BYTE_SEQUENCE) {
897        fatal("error decoding %s in file %s", encoding, id);
898    }    }
899    JSTokenStream * token_stream = js_NewTokenStream(context, base, input_length, NULL, 1, NULL);    JSTokenStream * token_stream = js_NewTokenStream(context, base, num_characters, NULL, 1, NULL);
900    if (token_stream == NULL) {    if (token_stream == NULL) {
901      fatal("cannot create token stream from file: %s", file_id);      fatal("cannot create token stream from file: %s", file_id);
902    }    }
# Line 830  Line 916 
916    An instrumented JavaScript file has 3 sections:    An instrumented JavaScript file has 3 sections:
917    1. initialization    1. initialization
918    2. instrumented source code    2. instrumented source code
919    3. original source code (TODO)    3. original source code
920    */    */
921    
922    Stream * instrumented = Stream_new(0);    Stream * instrumented = Stream_new(0);
# Line 855  Line 941 
941    Stream_write(output, instrumented->data, instrumented->length);    Stream_write(output, instrumented->data, instrumented->length);
942    Stream_write_char(output, '\n');    Stream_write_char(output, '\n');
943    
944    /* copy the original source to the output */    /* conditionals */
945      bool has_conditionals = false;
946      size_t line_number = 0;
947    size_t i = 0;    size_t i = 0;
948    while (i < input_length) {    while (i < num_characters) {
949        line_number++;
950        size_t line_start = i;
951        while (i < num_characters && base[i] != '\r' && base[i] != '\n') {
952          i++;
953        }
954        size_t line_end = i;
955        if (i < num_characters) {
956          if (base[i] == '\r') {
957            line_end = i;
958            i++;
959            if (i < num_characters && base[i] == '\n') {
960              i++;
961            }
962          }
963          else if (base[i] == '\n') {
964            line_end = i;
965            i++;
966          }
967          else {
968            abort();
969          }
970        }
971        char * line = js_DeflateString(context, base + line_start, line_end - line_start);
972        if (str_starts_with(line, "//#JSCOVERAGE_IF")) {
973          if (! has_conditionals) {
974            has_conditionals = true;
975            Stream_printf(output, "_$jscoverage['%s'].conditionals = [];\n", file_id);
976          }
977          Stream_printf(output, "if (!%s) {\n", line + 16);
978          Stream_printf(output, "  _$jscoverage['%s'].conditionals[%d] = ", file_id, line_number);
979        }
980        else if (str_starts_with(line, "//#JSCOVERAGE_ENDIF")) {
981          Stream_printf(output, "%d;\n", line_number);
982          Stream_printf(output, "}\n");
983        }
984        JS_free(context, line);
985      }
986    
987      /* copy the original source to the output */
988      i = 0;
989      while (i < num_characters) {
990      Stream_write_string(output, "// ");      Stream_write_string(output, "// ");
991      size_t line_start = i;      size_t line_start = i;
992      while (i < input_length && base[i] != '\r' && base[i] != '\n') {      while (i < num_characters && base[i] != '\r' && base[i] != '\n') {
993        i++;        i++;
994      }      }
995    
996      size_t line_end = i;      size_t line_end = i;
997      if (i < input_length) {      if (i < num_characters) {
998        if (base[i] == '\r') {        if (base[i] == '\r') {
999          line_end = i;          line_end = i;
1000          i++;          i++;
1001          if (i < input_length && base[i] == '\n') {          if (i < num_characters && base[i] == '\n') {
1002            i++;            i++;
1003          }          }
1004        }        }
# Line 894  Line 1023 
1023    
1024    file_id = NULL;    file_id = NULL;
1025  }  }
1026    
1027    void jscoverage_copy_resources(const char * destination_directory) {
1028      copy_resource("jscoverage.html", destination_directory);
1029      copy_resource("jscoverage.css", destination_directory);
1030      copy_resource("jscoverage.js", destination_directory);
1031      copy_resource("jscoverage-ie.css", destination_directory);
1032      copy_resource("jscoverage-throbber.gif", destination_directory);
1033      copy_resource("jscoverage-sh_main.js", destination_directory);
1034      copy_resource("jscoverage-sh_javascript.js", destination_directory);
1035      copy_resource("jscoverage-sh_nedit.css", destination_directory);
1036    }
1037    
1038    /*
1039    coverage reports
1040    */
1041    
1042    struct FileCoverageList {
1043      FileCoverage * file_coverage;
1044      struct FileCoverageList * next;
1045    };
1046    
1047    struct Coverage {
1048      JSHashTable * coverage_table;
1049      struct FileCoverageList * coverage_list;
1050    };
1051    
1052    static int compare_strings(const void * p1, const void * p2) {
1053      return strcmp(p1, p2) == 0;
1054    }
1055    
1056    Coverage * Coverage_new(void) {
1057      Coverage * result = xmalloc(sizeof(Coverage));
1058      result->coverage_table = JS_NewHashTable(1024, JS_HashString, compare_strings, NULL, NULL, NULL);
1059      if (result->coverage_table == NULL) {
1060        fatal("cannot create hash table");
1061      }
1062      result->coverage_list = NULL;
1063      return result;
1064    }
1065    
1066    void Coverage_delete(Coverage * coverage) {
1067      JS_HashTableDestroy(coverage->coverage_table);
1068      struct FileCoverageList * p = coverage->coverage_list;
1069      while (p != NULL) {
1070        free(p->file_coverage->lines);
1071        free(p->file_coverage->source);
1072        free(p->file_coverage->id);
1073        free(p->file_coverage);
1074        struct FileCoverageList * q = p;
1075        p = p->next;
1076        free(q);
1077      }
1078      free(coverage);
1079    }
1080    
1081    struct EnumeratorArg {
1082      CoverageForeachFunction f;
1083      void * p;
1084    };
1085    
1086    static intN enumerator(JSHashEntry * entry, intN i, void * arg) {
1087      struct EnumeratorArg * enumerator_arg = arg;
1088      enumerator_arg->f(entry->value, i, enumerator_arg->p);
1089      return 0;
1090    }
1091    
1092    void Coverage_foreach_file(Coverage * coverage, CoverageForeachFunction f, void * p) {
1093      struct EnumeratorArg enumerator_arg;
1094      enumerator_arg.f = f;
1095      enumerator_arg.p = p;
1096      JS_HashTableEnumerateEntries(coverage->coverage_table, enumerator, &enumerator_arg);
1097    }
1098    
1099    int jscoverage_parse_json(Coverage * coverage, const uint8_t * json, size_t length) {
1100      jschar * base = js_InflateString(context, (char *) json, &length);
1101      if (base == NULL) {
1102        fatal("out of memory");
1103      }
1104    
1105      jschar * parenthesized_json = xnew(jschar, addst(length, 2));
1106      parenthesized_json[0] = '(';
1107      memcpy(parenthesized_json + 1, base, mulst(length, sizeof(jschar)));
1108      parenthesized_json[length + 1] = ')';
1109    
1110      JS_free(context, base);
1111    
1112      JSTokenStream * token_stream = js_NewTokenStream(context, parenthesized_json, length + 2, NULL, 1, NULL);
1113      if (token_stream == NULL) {
1114        fatal("cannot create token stream");
1115      }
1116    
1117      JSParseNode * root = js_ParseTokenStream(context, global, token_stream);
1118      free(parenthesized_json);
1119      if (root == NULL) {
1120        return -1;
1121      }
1122    
1123      /* root node must be TOK_LC */
1124      if (root->pn_type != TOK_LC) {
1125        return -1;
1126      }
1127      JSParseNode * semi = root->pn_u.list.head;
1128    
1129      /* the list must be TOK_SEMI and it must contain only one element */
1130      if (semi->pn_type != TOK_SEMI || semi->pn_next != NULL) {
1131        return -1;
1132      }
1133      JSParseNode * parenthesized = semi->pn_kid;
1134    
1135      /* this must be a parenthesized expression */
1136      if (parenthesized->pn_type != TOK_RP) {
1137        return -1;
1138      }
1139      JSParseNode * object = parenthesized->pn_kid;
1140    
1141      /* this must be an object literal */
1142      if (object->pn_type != TOK_RC) {
1143        return -1;
1144      }
1145    
1146      for (JSParseNode * p = object->pn_head; p != NULL; p = p->pn_next) {
1147        /* every element of this list must be TOK_COLON */
1148        if (p->pn_type != TOK_COLON) {
1149          return -1;
1150        }
1151    
1152        /* the key must be a string representing the file */
1153        JSParseNode * key = p->pn_left;
1154        if (key->pn_type != TOK_STRING || ! ATOM_IS_STRING(key->pn_atom)) {
1155          return -1;
1156        }
1157        char * id_bytes = JS_GetStringBytes(ATOM_TO_STRING(key->pn_atom));
1158    
1159        /* the value must be an object literal OR an array */
1160        JSParseNode * value = p->pn_right;
1161        if (! (value->pn_type == TOK_RC || value->pn_type == TOK_RB)) {
1162          return -1;
1163        }
1164    
1165        JSParseNode * array = NULL;
1166        JSParseNode * source = NULL;
1167        if (value->pn_type == TOK_RB) {
1168          /* an array */
1169          array = value;
1170        }
1171        else if (value->pn_type == TOK_RC) {
1172          /* an object literal */
1173          if (value->pn_count != 2) {
1174            return -1;
1175          }
1176          for (JSParseNode * element = value->pn_head; element != NULL; element = element->pn_next) {
1177            if (element->pn_type != TOK_COLON) {
1178              return -1;
1179            }
1180            JSParseNode * left = element->pn_left;
1181            if (left->pn_type != TOK_STRING || ! ATOM_IS_STRING(left->pn_atom)) {
1182              return -1;
1183            }
1184            const char * s = JS_GetStringBytes(ATOM_TO_STRING(left->pn_atom));
1185            if (strcmp(s, "coverage") == 0) {
1186              array = element->pn_right;
1187              if (array->pn_type != TOK_RB) {
1188                return -1;
1189              }
1190            }
1191            else if (strcmp(s, "source") == 0) {
1192              source = element->pn_right;
1193              if (source->pn_type != TOK_STRING || ! ATOM_IS_STRING(source->pn_atom)) {
1194                return -1;
1195              }
1196            }
1197            else {
1198              return -1;
1199            }
1200          }
1201        }
1202        else {
1203          return -1;
1204        }
1205    
1206        if (array == NULL) {
1207          return -1;
1208        }
1209    
1210        /* look up the file in the coverage table */
1211        FileCoverage * file_coverage = JS_HashTableLookup(coverage->coverage_table, id_bytes);
1212        if (file_coverage == NULL) {
1213          /* not there: create a new one */
1214          char * id = xstrdup(id_bytes);
1215          file_coverage = xmalloc(sizeof(FileCoverage));
1216          file_coverage->id = id;
1217          file_coverage->num_lines = array->pn_count - 1;
1218          file_coverage->lines = xnew(int, array->pn_count);
1219          if (source == NULL) {
1220            file_coverage->source = NULL;
1221          }
1222          else {
1223            file_coverage->source = xstrdup(JS_GetStringBytes(ATOM_TO_STRING(source->pn_atom)));
1224          }
1225    
1226          /* set coverage for all lines */
1227          uint32 i = 0;
1228          for (JSParseNode * element = array->pn_head; element != NULL; element = element->pn_next, i++) {
1229            if (element->pn_type == TOK_NUMBER) {
1230              file_coverage->lines[i] = (int) element->pn_dval;
1231            }
1232            else if (element->pn_type == TOK_PRIMARY && element->pn_op == JSOP_NULL) {
1233              file_coverage->lines[i] = -1;
1234            }
1235            else {
1236              return -1;
1237            }
1238          }
1239          assert(i == array->pn_count);
1240    
1241          /* add to the hash table */
1242          JS_HashTableAdd(coverage->coverage_table, id, file_coverage);
1243          struct FileCoverageList * coverage_list = xmalloc(sizeof(struct FileCoverageList));
1244          coverage_list->file_coverage = file_coverage;
1245          coverage_list->next = coverage->coverage_list;
1246          coverage->coverage_list = coverage_list;
1247        }
1248        else {
1249          /* sanity check */
1250          assert(strcmp(file_coverage->id, id_bytes) == 0);
1251          if (file_coverage->num_lines != array->pn_count - 1) {
1252            return -2;
1253          }
1254    
1255          /* merge the coverage */
1256          uint32 i = 0;
1257          for (JSParseNode * element = array->pn_head; element != NULL; element = element->pn_next, i++) {
1258            if (element->pn_type == TOK_NUMBER) {
1259              if (file_coverage->lines[i] == -1) {
1260                return -2;
1261              }
1262              file_coverage->lines[i] += (int) element->pn_dval;
1263            }
1264            else if (element->pn_type == TOK_PRIMARY && element->pn_op == JSOP_NULL) {
1265              if (file_coverage->lines[i] != -1) {
1266                return -2;
1267              }
1268            }
1269            else {
1270              return -1;
1271            }
1272          }
1273          assert(i == array->pn_count);
1274    
1275          /* if this JSON file has source, use it */
1276          if (file_coverage->source == NULL && source != NULL) {
1277            file_coverage->source = xstrdup(JS_GetStringBytes(ATOM_TO_STRING(source->pn_atom)));
1278          }
1279        }
1280      }
1281    
1282      return 0;
1283    }

Legend:
Removed from v.95  
changed lines
  Added in v.176

  ViewVC Help
Powered by ViewVC 1.1.24