/[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 101 by siliconforks, Sat May 24 18:11:30 2008 UTC revision 116 by siliconforks, Sat May 31 21:42:36 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 "resource-manager.h"
38  #include "util.h"  #include "util.h"
39    
40  static JSRuntime * runtime = NULL;  static JSRuntime * runtime = NULL;
# Line 164  Line 167 
167    
168      /* function parameters */      /* function parameters */
169      Stream_write_string(f, "(");      Stream_write_string(f, "(");
170      JSAtom ** params = xmalloc(function->nargs * sizeof(JSAtom *));      JSAtom ** params = xnew(JSAtom *, function->nargs);
171      for (int i = 0; i < function->nargs; i++) {      for (int i = 0; i < function->nargs; i++) {
172        /* initialize to NULL for sanity check */        /* initialize to NULL for sanity check */
173        params[i] = NULL;        params[i] = NULL;
# Line 806  Line 809 
809    
810    /* scan the javascript */    /* scan the javascript */
811    size_t input_length = input->length;    size_t input_length = input->length;
812    jschar * base = js_InflateString(context, input->data, &input_length);    jschar * base = js_InflateString(context, (char *) input->data, &input_length);
813    if (base == NULL) {    if (base == NULL) {
814      fatal("out of memory");      fatal("out of memory");
815    }    }
# Line 830  Line 833 
833    An instrumented JavaScript file has 3 sections:    An instrumented JavaScript file has 3 sections:
834    1. initialization    1. initialization
835    2. instrumented source code    2. instrumented source code
836    3. original source code (TODO)    3. original source code
837    */    */
838    
839    Stream * instrumented = Stream_new(0);    Stream * instrumented = Stream_new(0);
# Line 894  Line 897 
897    
898    file_id = NULL;    file_id = NULL;
899  }  }
900    
901    void jscoverage_copy_resources(const char * destination_directory) {
902      copy_resource("jscoverage.html", destination_directory);
903      copy_resource("jscoverage.css", destination_directory);
904      copy_resource("jscoverage.js", destination_directory);
905      copy_resource("jscoverage-throbber.gif", destination_directory);
906      copy_resource("jscoverage-sh_main.js", destination_directory);
907      copy_resource("jscoverage-sh_javascript.js", destination_directory);
908      copy_resource("jscoverage-sh_nedit.css", destination_directory);
909    }
910    
911    /*
912    coverage reports
913    */
914    
915    struct FileCoverageList {
916      FileCoverage * file_coverage;
917      struct FileCoverageList * next;
918    };
919    
920    struct Coverage {
921      JSHashTable * coverage_table;
922      struct FileCoverageList * coverage_list;
923    };
924    
925    static int compare_strings(const void * p1, const void * p2) {
926      return strcmp(p1, p2) == 0;
927    }
928    
929    Coverage * Coverage_new(void) {
930      Coverage * result = xmalloc(sizeof(Coverage));
931      result->coverage_table = JS_NewHashTable(1024, JS_HashString, compare_strings, NULL, NULL, NULL);
932      if (result->coverage_table == NULL) {
933        fatal("cannot create hash table");
934      }
935      result->coverage_list = NULL;
936      return result;
937    }
938    
939    void Coverage_delete(Coverage * coverage) {
940      JS_HashTableDestroy(coverage->coverage_table);
941      struct FileCoverageList * p = coverage->coverage_list;
942      while (p != NULL) {
943        free(p->file_coverage->lines);
944        free(p->file_coverage->source);
945        free(p->file_coverage->id);
946        free(p->file_coverage);
947        struct FileCoverageList * q = p;
948        p = p->next;
949        free(q);
950      }
951      free(coverage);
952    }
953    
954    struct EnumeratorArg {
955      CoverageForeachFunction f;
956      void * p;
957    };
958    
959    static intN enumerator(JSHashEntry * entry, intN i, void * arg) {
960      struct EnumeratorArg * enumerator_arg = arg;
961      enumerator_arg->f(entry->value, i, enumerator_arg->p);
962      return 0;
963    }
964    
965    void Coverage_foreach_file(Coverage * coverage, CoverageForeachFunction f, void * p) {
966      struct EnumeratorArg enumerator_arg;
967      enumerator_arg.f = f;
968      enumerator_arg.p = p;
969      JS_HashTableEnumerateEntries(coverage->coverage_table, enumerator, &enumerator_arg);
970    }
971    
972    int jscoverage_parse_json(Coverage * coverage, const uint8_t * json, size_t length) {
973      jschar * base = js_InflateString(context, (char *) json, &length);
974      if (base == NULL) {
975        fatal("out of memory");
976      }
977    
978      jschar * parenthesized_json = xnew(jschar, addst(length, 2));
979      parenthesized_json[0] = '(';
980      memcpy(parenthesized_json + 1, base, mulst(length, sizeof(jschar)));
981      parenthesized_json[length + 1] = ')';
982    
983      JS_free(context, base);
984    
985      JSTokenStream * token_stream = js_NewTokenStream(context, parenthesized_json, length + 2, NULL, 1, NULL);
986      if (token_stream == NULL) {
987        fatal("cannot create token stream");
988      }
989    
990      JSParseNode * root = js_ParseTokenStream(context, global, token_stream);
991      free(parenthesized_json);
992      if (root == NULL) {
993        return -1;
994      }
995    
996      /* root node must be TOK_LC */
997      if (root->pn_type != TOK_LC) {
998        return -1;
999      }
1000      JSParseNode * semi = root->pn_u.list.head;
1001    
1002      /* the list must be TOK_SEMI and it must contain only one element */
1003      if (semi->pn_type != TOK_SEMI || semi->pn_next != NULL) {
1004        return -1;
1005      }
1006      JSParseNode * parenthesized = semi->pn_kid;
1007    
1008      /* this must be a parenthesized expression */
1009      if (parenthesized->pn_type != TOK_RP) {
1010        return -1;
1011      }
1012      JSParseNode * object = parenthesized->pn_kid;
1013    
1014      /* this must be an object literal */
1015      if (object->pn_type != TOK_RC) {
1016        return -1;
1017      }
1018    
1019      for (JSParseNode * p = object->pn_head; p != NULL; p = p->pn_next) {
1020        /* every element of this list must be TOK_COLON */
1021        if (p->pn_type != TOK_COLON) {
1022          return -1;
1023        }
1024    
1025        /* the key must be a string representing the file */
1026        JSParseNode * key = p->pn_left;
1027        if (key->pn_type != TOK_STRING || ! ATOM_IS_STRING(key->pn_atom)) {
1028          return -1;
1029        }
1030        char * id_bytes = JS_GetStringBytes(ATOM_TO_STRING(key->pn_atom));
1031    
1032        /* the value must be an object literal OR an array */
1033        JSParseNode * value = p->pn_right;
1034        if (! (value->pn_type == TOK_RC || value->pn_type == TOK_RB)) {
1035          return -1;
1036        }
1037    
1038        JSParseNode * array = NULL;
1039        JSParseNode * source = NULL;
1040        if (value->pn_type == TOK_RB) {
1041          /* an array */
1042          array = value;
1043        }
1044        else if (value->pn_type == TOK_RC) {
1045          /* an object literal */
1046          if (value->pn_count != 2) {
1047            return -1;
1048          }
1049          for (JSParseNode * element = value->pn_head; element != NULL; element = element->pn_next) {
1050            if (element->pn_type != TOK_COLON) {
1051              return -1;
1052            }
1053            JSParseNode * left = element->pn_left;
1054            if (left->pn_type != TOK_STRING || ! ATOM_IS_STRING(left->pn_atom)) {
1055              return -1;
1056            }
1057            const char * s = JS_GetStringBytes(ATOM_TO_STRING(left->pn_atom));
1058            if (strcmp(s, "coverage") == 0) {
1059              array = element->pn_right;
1060              if (array->pn_type != TOK_RB) {
1061                return -1;
1062              }
1063            }
1064            else if (strcmp(s, "source") == 0) {
1065              source = element->pn_right;
1066              if (source->pn_type != TOK_STRING || ! ATOM_IS_STRING(source->pn_atom)) {
1067                return -1;
1068              }
1069            }
1070            else {
1071              return -1;
1072            }
1073          }
1074        }
1075        else {
1076          return -1;
1077        }
1078    
1079        if (array == NULL) {
1080          return -1;
1081        }
1082    
1083        /* look up the file in the coverage table */
1084        FileCoverage * file_coverage = JS_HashTableLookup(coverage->coverage_table, id_bytes);
1085        if (file_coverage == NULL) {
1086          /* not there: create a new one */
1087          char * id = xstrdup(id_bytes);
1088          file_coverage = xmalloc(sizeof(FileCoverage));
1089          file_coverage->id = id;
1090          file_coverage->num_lines = array->pn_count - 1;
1091          file_coverage->lines = xnew(int, array->pn_count);
1092          if (source == NULL) {
1093            file_coverage->source = NULL;
1094          }
1095          else {
1096            file_coverage->source = xstrdup(JS_GetStringBytes(ATOM_TO_STRING(source->pn_atom)));
1097          }
1098    
1099          /* set coverage for all lines */
1100          uint32 i = 0;
1101          for (JSParseNode * element = array->pn_head; element != NULL; element = element->pn_next, i++) {
1102            if (element->pn_type == TOK_NUMBER) {
1103              file_coverage->lines[i] = (int) element->pn_dval;
1104            }
1105            else if (element->pn_type == TOK_PRIMARY && element->pn_op == JSOP_NULL) {
1106              file_coverage->lines[i] = -1;
1107            }
1108            else {
1109              return -1;
1110            }
1111          }
1112          assert(i == array->pn_count);
1113    
1114          /* add to the hash table */
1115          JS_HashTableAdd(coverage->coverage_table, id, file_coverage);
1116          struct FileCoverageList * coverage_list = xmalloc(sizeof(struct FileCoverageList));
1117          coverage_list->file_coverage = file_coverage;
1118          coverage_list->next = coverage->coverage_list;
1119          coverage->coverage_list = coverage_list;
1120        }
1121        else {
1122          /* sanity check */
1123          assert(strcmp(file_coverage->id, id_bytes) == 0);
1124          if (file_coverage->num_lines != array->pn_count - 1) {
1125            return -2;
1126          }
1127    
1128          /* merge the coverage */
1129          uint32 i = 0;
1130          for (JSParseNode * element = array->pn_head; element != NULL; element = element->pn_next, i++) {
1131            if (element->pn_type == TOK_NUMBER) {
1132              if (file_coverage->lines[i] == -1) {
1133                return -2;
1134              }
1135              file_coverage->lines[i] += (int) element->pn_dval;
1136            }
1137            else if (element->pn_type == TOK_PRIMARY && element->pn_op == JSOP_NULL) {
1138              if (file_coverage->lines[i] != -1) {
1139                return -2;
1140              }
1141            }
1142            else {
1143              return -1;
1144            }
1145          }
1146          assert(i == array->pn_count);
1147    
1148          /* if this JSON file has source, use it */
1149          if (file_coverage->source == NULL && source != NULL) {
1150            file_coverage->source = xstrdup(JS_GetStringBytes(ATOM_TO_STRING(source->pn_atom)));
1151          }
1152        }
1153      }
1154    
1155      return 0;
1156    }

Legend:
Removed from v.101  
changed lines
  Added in v.116

  ViewVC Help
Powered by ViewVC 1.1.24