/[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 116 by siliconforks, Sat May 31 21:42:36 2008 UTC revision 176 by siliconforks, Sat Sep 20 23:29:42 2008 UTC
# Line 34  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"  #include "resource-manager.h"
39  #include "util.h"  #include "util.h"
40    
# Line 75  Line 76 
76  }  }
77    
78  static void print_string(JSString * s, Stream * f) {  static void print_string(JSString * s, Stream * f) {
79    for (size_t 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 87  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 148  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 = xnew(JSAtom *, function->nargs);    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 228  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 435  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 464  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 541  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 804  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, (char *) 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 858  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 902  Line 1028 
1028    copy_resource("jscoverage.html", destination_directory);    copy_resource("jscoverage.html", destination_directory);
1029    copy_resource("jscoverage.css", destination_directory);    copy_resource("jscoverage.css", destination_directory);
1030    copy_resource("jscoverage.js", destination_directory);    copy_resource("jscoverage.js", destination_directory);
1031      copy_resource("jscoverage-ie.css", destination_directory);
1032    copy_resource("jscoverage-throbber.gif", destination_directory);    copy_resource("jscoverage-throbber.gif", destination_directory);
1033    copy_resource("jscoverage-sh_main.js", destination_directory);    copy_resource("jscoverage-sh_main.js", destination_directory);
1034    copy_resource("jscoverage-sh_javascript.js", destination_directory);    copy_resource("jscoverage-sh_javascript.js", destination_directory);

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

  ViewVC Help
Powered by ViewVC 1.1.24