/[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 156 by siliconforks, Fri Sep 12 19:44:03 2008 UTC revision 392 by siliconforks, Thu Oct 30 17:54:24 2008 UTC
# Line 22  Line 22 
22  #include "instrument-js.h"  #include "instrument-js.h"
23    
24  #include <assert.h>  #include <assert.h>
25    #include <math.h>
26  #include <stdlib.h>  #include <stdlib.h>
27  #include <string.h>  #include <string.h>
28    
29  #include <jsapi.h>  #include <jsapi.h>
30    #include <jsarena.h>
31  #include <jsatom.h>  #include <jsatom.h>
32    #include <jsemit.h>
33    #include <jsexn.h>
34  #include <jsfun.h>  #include <jsfun.h>
35  #include <jsinterp.h>  #include <jsinterp.h>
36    #include <jsiter.h>
37  #include <jsparse.h>  #include <jsparse.h>
38  #include <jsregexp.h>  #include <jsregexp.h>
39  #include <jsscope.h>  #include <jsscope.h>
40  #include <jsstr.h>  #include <jsstr.h>
41    
42    #include "encoding.h"
43    #include "global.h"
44    #include "highlight.h"
45  #include "resource-manager.h"  #include "resource-manager.h"
46  #include "util.h"  #include "util.h"
47    
48    struct IfDirective {
49      const jschar * condition_start;
50      const jschar * condition_end;
51      uint16_t start_line;
52      uint16_t end_line;
53      struct IfDirective * next;
54    };
55    
56    bool jscoverage_mozilla = false;
57    
58    static bool * exclusive_directives = NULL;
59    
60  static JSRuntime * runtime = NULL;  static JSRuntime * runtime = NULL;
61  static JSContext * context = NULL;  static JSContext * context = NULL;
62  static JSObject * global = NULL;  static JSObject * global = NULL;
63    static JSVersion js_version = JSVERSION_ECMA_3;
64    
65  /*  /*
66  JSParseNode objects store line numbers starting from 1.  JSParseNode objects store line numbers starting from 1.
# Line 47  Line 68 
68  */  */
69  static const char * file_id = NULL;  static const char * file_id = NULL;
70  static char * lines = NULL;  static char * lines = NULL;
71    static uint16_t num_lines = 0;
72    
73    void jscoverage_set_js_version(const char * version) {
74      js_version = JS_StringToVersion(version);
75      if (js_version != JSVERSION_UNKNOWN) {
76        return;
77      }
78    
79      char * end;
80      js_version = (JSVersion) strtol(version, &end, 10);
81      if ((size_t) (end - version) != strlen(version)) {
82        fatal("invalid version: %s", version);
83      }
84    }
85    
86  void jscoverage_init(void) {  void jscoverage_init(void) {
87    runtime = JS_NewRuntime(8L * 1024L * 1024L);    runtime = JS_NewRuntime(8L * 1024L * 1024L);
# Line 59  Line 94 
94      fatal("cannot create context");      fatal("cannot create context");
95    }    }
96    
97      JS_SetVersion(context, js_version);
98    
99    global = JS_NewObject(context, NULL, NULL, NULL);    global = JS_NewObject(context, NULL, NULL, NULL);
100    if (global == NULL) {    if (global == NULL) {
101      fatal("cannot create global object");      fatal("cannot create global object");
# Line 74  Line 111 
111    JS_DestroyRuntime(runtime);    JS_DestroyRuntime(runtime);
112  }  }
113    
114    static void print_javascript(const jschar * characters, size_t num_characters, Stream * f) {
115      for (size_t i = 0; i < num_characters; i++) {
116        jschar c = characters[i];
117        /*
118        XXX does not handle no-break space, other unicode "space separator"
119        */
120        switch (c) {
121        case 0x9:
122        case 0xB:
123        case 0xC:
124          Stream_write_char(f, c);
125          break;
126        default:
127          if (32 <= c && c <= 126) {
128            Stream_write_char(f, c);
129          }
130          else {
131            Stream_printf(f, "\\u%04x", c);
132          }
133          break;
134        }
135      }
136    }
137    
138  static void print_string(JSString * s, Stream * f) {  static void print_string(JSString * s, Stream * f) {
139    for (size_t i = 0; i < s->length; i++) {    size_t length = JSSTRING_LENGTH(s);
140      char c = s->chars[i];    jschar * characters = JSSTRING_CHARS(s);
141      Stream_write_char(f, c);    for (size_t i = 0; i < length; i++) {
142        jschar c = characters[i];
143        if (32 <= c && c <= 126) {
144          switch (c) {
145          case '"':
146            Stream_write_string(f, "\\\"");
147            break;
148    /*
149          case '\'':
150            Stream_write_string(f, "\\'");
151            break;
152    */
153          case '\\':
154            Stream_write_string(f, "\\\\");
155            break;
156          default:
157            Stream_write_char(f, c);
158            break;
159          }
160        }
161        else {
162          switch (c) {
163          case 0x8:
164            Stream_write_string(f, "\\b");
165            break;
166          case 0x9:
167            Stream_write_string(f, "\\t");
168            break;
169          case 0xa:
170            Stream_write_string(f, "\\n");
171            break;
172          /* IE doesn't support this */
173          /*
174          case 0xb:
175            Stream_write_string(f, "\\v");
176            break;
177          */
178          case 0xc:
179            Stream_write_string(f, "\\f");
180            break;
181          case 0xd:
182            Stream_write_string(f, "\\r");
183            break;
184          default:
185            Stream_printf(f, "\\u%04x", c);
186            break;
187          }
188        }
189    }    }
190  }  }
191    
# Line 87  Line 195 
195    print_string(s, f);    print_string(s, f);
196  }  }
197    
198  static void print_string_jsval(jsval value, Stream * f) {  static void print_regex(jsval value, Stream * f) {
199    assert(JSVAL_IS_STRING(value));    assert(JSVAL_IS_STRING(value));
200    JSString * s = JSVAL_TO_STRING(value);    JSString * s = JSVAL_TO_STRING(value);
201    print_string(s, f);    size_t length = JSSTRING_LENGTH(s);
202      jschar * characters = JSSTRING_CHARS(s);
203      for (size_t i = 0; i < length; i++) {
204        jschar c = characters[i];
205        if (32 <= c && c <= 126) {
206          Stream_write_char(f, c);
207        }
208        else {
209          Stream_printf(f, "\\u%04x", c);
210        }
211      }
212  }  }
213    
214  static void print_quoted_string_atom(JSAtom * atom, Stream * f) {  static void print_quoted_string_atom(JSAtom * atom, Stream * f) {
215    assert(ATOM_IS_STRING(atom));    assert(ATOM_IS_STRING(atom));
216    JSString * s = ATOM_TO_STRING(atom);    JSString * s = ATOM_TO_STRING(atom);
217    JSString * quoted = js_QuoteString(context, s, '"');    Stream_write_char(f, '"');
218    print_string(quoted, f);    print_string(s, f);
219      Stream_write_char(f, '"');
220  }  }
221    
222  static const char * get_op(uint8 op) {  static const char * get_op(uint8 op) {
223    switch(op) {    switch(op) {
224      case JSOP_OR:
225        return "||";
226      case JSOP_AND:
227        return "&&";
228    case JSOP_BITOR:    case JSOP_BITOR:
229      return "|";      return "|";
230    case JSOP_BITXOR:    case JSOP_BITXOR:
# Line 112  Line 235 
235      return "==";      return "==";
236    case JSOP_NE:    case JSOP_NE:
237      return "!=";      return "!=";
238    case JSOP_NEW_EQ:    case JSOP_STRICTEQ:
239      return "===";      return "===";
240    case JSOP_NEW_NE:    case JSOP_STRICTNE:
241      return "!==";      return "!==";
242    case JSOP_LT:    case JSOP_LT:
243      return "<";      return "<";
# Line 145  Line 268 
268    }    }
269  }  }
270    
271  static void instrument_expression(JSParseNode * node, Stream * f);  static void output_expression(JSParseNode * node, Stream * f, bool parenthesize_object_literals);
272  static void instrument_statement(JSParseNode * node, Stream * f, int indent);  static void instrument_statement(JSParseNode * node, Stream * f, int indent, bool is_jscoverage_if);
273    static void output_statement(JSParseNode * node, Stream * f, int indent, bool is_jscoverage_if);
274    
275  enum FunctionType {  enum FunctionType {
276    FUNCTION_NORMAL,    FUNCTION_NORMAL,
277    FUNCTION_GETTER_OR_SETTER    FUNCTION_GETTER_OR_SETTER
278  };  };
279    
280    static void output_for_in(JSParseNode * node, Stream * f) {
281      assert(node->pn_type == TOK_FOR);
282      assert(node->pn_arity == PN_BINARY);
283      Stream_write_string(f, "for ");
284      if (node->pn_iflags & JSITER_FOREACH) {
285        Stream_write_string(f, "each ");
286      }
287      Stream_write_char(f, '(');
288      output_expression(node->pn_left, f, false);
289      Stream_write_char(f, ')');
290    }
291    
292    static void output_array_comprehension_or_generator_expression(JSParseNode * node, Stream * f) {
293      assert(node->pn_type == TOK_LEXICALSCOPE);
294      assert(node->pn_arity == PN_NAME);
295      JSParseNode * for_node = node->pn_expr;
296      assert(for_node->pn_type == TOK_FOR);
297      assert(for_node->pn_arity == PN_BINARY);
298      JSParseNode * p = for_node;
299      while (p->pn_type == TOK_FOR) {
300        p = p->pn_right;
301      }
302      JSParseNode * if_node = NULL;
303      if (p->pn_type == TOK_IF) {
304        if_node = p;
305        assert(if_node->pn_arity == PN_TERNARY);
306        p = if_node->pn_kid2;
307      }
308      assert(p->pn_arity == PN_UNARY);
309      p = p->pn_kid;
310      if (p->pn_type == TOK_YIELD) {
311        /* for generator expressions */
312        p = p->pn_kid;
313      }
314    
315      output_expression(p, f, false);
316      p = for_node;
317      while (p->pn_type == TOK_FOR) {
318        Stream_write_char(f, ' ');
319        output_for_in(p, f);
320        p = p->pn_right;
321      }
322      if (if_node) {
323        Stream_write_string(f, " if (");
324        output_expression(if_node->pn_kid1, f, false);
325        Stream_write_char(f, ')');
326      }
327    }
328    
329  static void instrument_function(JSParseNode * node, Stream * f, int indent, enum FunctionType type) {  static void instrument_function(JSParseNode * node, Stream * f, int indent, enum FunctionType type) {
330      assert(node->pn_type == TOK_FUNCTION);
331    assert(node->pn_arity == PN_FUNC);    assert(node->pn_arity == PN_FUNC);
332    assert(ATOM_IS_OBJECT(node->pn_funAtom));    JSObject * object = node->pn_funpob->object;
   JSObject * object = ATOM_TO_OBJECT(node->pn_funAtom);  
333    assert(JS_ObjectIsFunction(context, object));    assert(JS_ObjectIsFunction(context, object));
334    JSFunction * function = (JSFunction *) JS_GetPrivate(context, object);    JSFunction * function = (JSFunction *) JS_GetPrivate(context, object);
335    assert(function);    assert(function);
336    assert(object == function->object);    assert(object == &function->object);
337    Stream_printf(f, "%*s", indent, "");    Stream_printf(f, "%*s", indent, "");
338    if (type == FUNCTION_NORMAL) {    if (type == FUNCTION_NORMAL) {
339      Stream_write_string(f, "function");      Stream_write_string(f, "function ");
340    }    }
341    
342    /* function name */    /* function name */
343    if (function->atom) {    if (function->atom) {
     Stream_write_char(f, ' ');  
344      print_string_atom(function->atom, f);      print_string_atom(function->atom, f);
345    }    }
346    
347    /* function parameters */    /*
348    Stream_write_string(f, "(");    function parameters - see JS_DecompileFunction in jsapi.cpp, which calls
349    JSAtom ** params = xnew(JSAtom *, function->nargs);    js_DecompileFunction in jsopcode.cpp
350    for (int i = 0; i < function->nargs; i++) {    */
351      /* initialize to NULL for sanity check */    Stream_write_char(f, '(');
352      params[i] = NULL;    JSArenaPool pool;
353    }    JS_INIT_ARENA_POOL(&pool, "instrument_function", 256, 1, &context->scriptStackQuota);
354    JSScope * scope = OBJ_SCOPE(object);    jsuword * local_names = NULL;
355    for (JSScopeProperty * scope_property = SCOPE_LAST_PROP(scope); scope_property != NULL; scope_property = scope_property->parent) {    if (JS_GET_LOCAL_NAME_COUNT(function)) {
356      if (scope_property->getter != js_GetArgument) {      local_names = js_GetLocalNameArray(context, function, &pool);
357        continue;      if (local_names == NULL) {
358          fatal("out of memory");
359      }      }
     assert(scope_property->flags & SPROP_HAS_SHORTID);  
     assert((uint16) scope_property->shortid < function->nargs);  
     assert(JSID_IS_ATOM(scope_property->id));  
     params[(uint16) scope_property->shortid] = JSID_TO_ATOM(scope_property->id);  
360    }    }
361      bool destructuring = false;
362    for (int i = 0; i < function->nargs; i++) {    for (int i = 0; i < function->nargs; i++) {
     assert(params[i] != NULL);  
363      if (i > 0) {      if (i > 0) {
364        Stream_write_string(f, ", ");        Stream_write_string(f, ", ");
365      }      }
366      if (ATOM_IS_STRING(params[i])) {      JSAtom * param = JS_LOCAL_NAME_TO_ATOM(local_names[i]);
367        print_string_atom(params[i], f);      if (param == NULL) {
368          destructuring = true;
369          JSParseNode * expression = NULL;
370          assert(node->pn_body->pn_type == TOK_LC || node->pn_body->pn_type == TOK_BODY);
371          JSParseNode * semi = node->pn_body->pn_head;
372          assert(semi->pn_type == TOK_SEMI);
373          JSParseNode * comma = semi->pn_kid;
374          assert(comma->pn_type == TOK_COMMA);
375          for (JSParseNode * p = comma->pn_head; p != NULL; p = p->pn_next) {
376            assert(p->pn_type == TOK_ASSIGN);
377            JSParseNode * rhs = p->pn_right;
378            assert(JSSTRING_LENGTH(ATOM_TO_STRING(rhs->pn_atom)) == 0);
379            if (rhs->pn_slot == i) {
380              expression = p->pn_left;
381              break;
382            }
383          }
384          assert(expression != NULL);
385          output_expression(expression, f, false);
386        }
387        else {
388          print_string_atom(param, f);
389      }      }
390    }    }
391      JS_FinishArenaPool(&pool);
392    Stream_write_string(f, ") {\n");    Stream_write_string(f, ") {\n");
   free(params);  
393    
394    /* function body */    /* function body */
395    instrument_statement(node->pn_body, f, indent + 2);    if (function->flags & JSFUN_EXPR_CLOSURE) {
396        /* expression closure - use output_statement instead of instrument_statement */
397        if (node->pn_body->pn_type == TOK_BODY) {
398          assert(node->pn_body->pn_arity == PN_LIST);
399          assert(node->pn_body->pn_count == 2);
400          output_statement(node->pn_body->pn_head->pn_next, f, indent + 2, false);
401        }
402        else {
403          output_statement(node->pn_body, f, indent + 2, false);
404        }
405      }
406      else {
407        assert(node->pn_body->pn_type == TOK_LC);
408        assert(node->pn_body->pn_arity == PN_LIST);
409        JSParseNode * p = node->pn_body->pn_head;
410        if (destructuring) {
411          p = p->pn_next;
412        }
413        for (; p != NULL; p = p->pn_next) {
414          instrument_statement(p, f, indent + 2, false);
415        }
416      }
417    
418    Stream_write_string(f, "}\n");    Stream_write_char(f, '}');
419  }  }
420    
421  static void instrument_function_call(JSParseNode * node, Stream * f) {  static void instrument_function_call(JSParseNode * node, Stream * f) {
422    instrument_expression(node->pn_head, f);    JSParseNode * function_node = node->pn_head;
423      if (function_node->pn_type == TOK_FUNCTION) {
424        JSObject * object = function_node->pn_funpob->object;
425        assert(JS_ObjectIsFunction(context, object));
426        JSFunction * function = (JSFunction *) JS_GetPrivate(context, object);
427        assert(function);
428        assert(object == &function->object);
429    
430        if (function_node->pn_flags & TCF_GENEXP_LAMBDA) {
431          /* it's a generator expression */
432          Stream_write_char(f, '(');
433          output_array_comprehension_or_generator_expression(function_node->pn_body, f);
434          Stream_write_char(f, ')');
435          return;
436        }
437      }
438      output_expression(function_node, f, false);
439    Stream_write_char(f, '(');    Stream_write_char(f, '(');
440    for (struct JSParseNode * p = node->pn_head->pn_next; p != NULL; p = p->pn_next) {    for (struct JSParseNode * p = function_node->pn_next; p != NULL; p = p->pn_next) {
441      if (p != node->pn_head->pn_next) {      if (p != node->pn_head->pn_next) {
442        Stream_write_string(f, ", ");        Stream_write_string(f, ", ");
443      }      }
444      instrument_expression(p, f);      output_expression(p, f, false);
445    }    }
446    Stream_write_char(f, ')');    Stream_write_char(f, ')');
447  }  }
448    
449    static void instrument_declarations(JSParseNode * list, Stream * f) {
450      assert(list->pn_arity == PN_LIST);
451      for (JSParseNode * p = list->pn_head; p != NULL; p = p->pn_next) {
452        if (p != list->pn_head) {
453          Stream_write_string(f, ", ");
454        }
455        output_expression(p, f, false);
456      }
457    }
458    
459  /*  /*
460  See <Expressions> in jsparse.h.  See <Expressions> in jsparse.h.
461  TOK_FUNCTION is handled as a statement and as an expression.  TOK_FUNCTION is handled as a statement and as an expression.
# Line 232  Line 469 
469  TOK_INSTANCEOF  binary  TOK_INSTANCEOF  binary
470  TOK_IN          binary  TOK_IN          binary
471  */  */
472  static void instrument_expression(JSParseNode * node, Stream * f) {  static void output_expression(JSParseNode * node, Stream * f, bool parenthesize_object_literals) {
473    switch (node->pn_type) {    switch (node->pn_type) {
474    case TOK_FUNCTION:    case TOK_FUNCTION:
475        Stream_write_char(f, '(');
476      instrument_function(node, f, 0, FUNCTION_NORMAL);      instrument_function(node, f, 0, FUNCTION_NORMAL);
477        Stream_write_char(f, ')');
478      break;      break;
479    case TOK_COMMA:    case TOK_COMMA:
480      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) {
481        if (p != node->pn_head) {        if (p != node->pn_head) {
482          Stream_write_string(f, ", ");          Stream_write_string(f, ", ");
483        }        }
484        instrument_expression(p, f);        output_expression(p, f, parenthesize_object_literals);
485      }      }
486      break;      break;
487    case TOK_ASSIGN:    case TOK_ASSIGN:
488      instrument_expression(node->pn_left, f);      output_expression(node->pn_left, f, parenthesize_object_literals);
489      Stream_write_char(f, ' ');      Stream_write_char(f, ' ');
490      switch (node->pn_op) {      switch (node->pn_op) {
491      case JSOP_ADD:      case JSOP_ADD:
# Line 267  Line 506 
506        break;        break;
507      }      }
508      Stream_write_string(f, "= ");      Stream_write_string(f, "= ");
509      instrument_expression(node->pn_right, f);      output_expression(node->pn_right, f, false);
510      break;      break;
511    case TOK_HOOK:    case TOK_HOOK:
512      instrument_expression(node->pn_kid1, f);      output_expression(node->pn_kid1, f, parenthesize_object_literals);
513      Stream_write_string(f, "? ");      Stream_write_string(f, "? ");
514      instrument_expression(node->pn_kid2, f);      output_expression(node->pn_kid2, f, false);
515      Stream_write_string(f, ": ");      Stream_write_string(f, ": ");
516      instrument_expression(node->pn_kid3, f);      output_expression(node->pn_kid3, f, false);
517      break;      break;
518    case TOK_OR:    case TOK_OR:
     instrument_expression(node->pn_left, f);  
     Stream_write_string(f, " || ");  
     instrument_expression(node->pn_right, f);  
     break;  
519    case TOK_AND:    case TOK_AND:
     instrument_expression(node->pn_left, f);  
     Stream_write_string(f, " && ");  
     instrument_expression(node->pn_right, f);  
     break;  
520    case TOK_BITOR:    case TOK_BITOR:
521    case TOK_BITXOR:    case TOK_BITXOR:
522    case TOK_BITAND:    case TOK_BITAND:
# Line 298  Line 529 
529    case TOK_DIVOP:    case TOK_DIVOP:
530      switch (node->pn_arity) {      switch (node->pn_arity) {
531      case PN_BINARY:      case PN_BINARY:
532        instrument_expression(node->pn_left, f);        output_expression(node->pn_left, f, parenthesize_object_literals);
533        Stream_printf(f, " %s ", get_op(node->pn_op));        Stream_printf(f, " %s ", get_op(node->pn_op));
534        instrument_expression(node->pn_right, f);        output_expression(node->pn_right, f, false);
535        break;        break;
536      case PN_LIST:      case PN_LIST:
537        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) {
538          if (p != node->pn_head) {          if (p == node->pn_head) {
539              output_expression(p, f, parenthesize_object_literals);
540            }
541            else {
542            Stream_printf(f, " %s ", get_op(node->pn_op));            Stream_printf(f, " %s ", get_op(node->pn_op));
543              output_expression(p, f, false);
544          }          }
         instrument_expression(p, f);  
545        }        }
546        break;        break;
547      default:      default:
# Line 317  Line 551 
551    case TOK_UNARYOP:    case TOK_UNARYOP:
552      switch (node->pn_op) {      switch (node->pn_op) {
553      case JSOP_NEG:      case JSOP_NEG:
554        Stream_write_char(f, '-');        Stream_write_string(f, "- ");
555        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, false);
556        break;        break;
557      case JSOP_POS:      case JSOP_POS:
558        Stream_write_char(f, '+');        Stream_write_string(f, "+ ");
559        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, false);
560        break;        break;
561      case JSOP_NOT:      case JSOP_NOT:
562        Stream_write_char(f, '!');        Stream_write_string(f, "! ");
563        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, false);
564        break;        break;
565      case JSOP_BITNOT:      case JSOP_BITNOT:
566        Stream_write_char(f, '~');        Stream_write_string(f, "~ ");
567        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, false);
568        break;        break;
569      case JSOP_TYPEOF:      case JSOP_TYPEOF:
570        Stream_write_string(f, "typeof ");        Stream_write_string(f, "typeof ");
571        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, false);
572        break;        break;
573      case JSOP_VOID:      case JSOP_VOID:
574        Stream_write_string(f, "void ");        Stream_write_string(f, "void ");
575        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, false);
576        break;        break;
577      default:      default:
578        abort();        fatal_source(file_id, node->pn_pos.begin.lineno, "unknown operator (%d)", node->pn_op);
579        break;        break;
580      }      }
581      break;      break;
# Line 355  Line 589 
589      case JSOP_INCPROP:      case JSOP_INCPROP:
590      case JSOP_INCELEM:      case JSOP_INCELEM:
591        Stream_write_string(f, "++");        Stream_write_string(f, "++");
592        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, false);
593        break;        break;
594      case JSOP_DECNAME:      case JSOP_DECNAME:
595      case JSOP_DECPROP:      case JSOP_DECPROP:
596      case JSOP_DECELEM:      case JSOP_DECELEM:
597        Stream_write_string(f, "--");        Stream_write_string(f, "--");
598        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, false);
599        break;        break;
600      case JSOP_NAMEINC:      case JSOP_NAMEINC:
601      case JSOP_PROPINC:      case JSOP_PROPINC:
602      case JSOP_ELEMINC:      case JSOP_ELEMINC:
603        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, parenthesize_object_literals);
604        Stream_write_string(f, "++");        Stream_write_string(f, "++");
605        break;        break;
606      case JSOP_NAMEDEC:      case JSOP_NAMEDEC:
607      case JSOP_PROPDEC:      case JSOP_PROPDEC:
608      case JSOP_ELEMDEC:      case JSOP_ELEMDEC:
609        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, parenthesize_object_literals);
610        Stream_write_string(f, "--");        Stream_write_string(f, "--");
611        break;        break;
612      default:      default:
# Line 386  Line 620 
620      break;      break;
621    case TOK_DELETE:    case TOK_DELETE:
622      Stream_write_string(f, "delete ");      Stream_write_string(f, "delete ");
623      instrument_expression(node->pn_kid, f);      output_expression(node->pn_kid, f, false);
624      break;      break;
625    case TOK_DOT:    case TOK_DOT:
626        /* numeric literals must be parenthesized */
627        switch (node->pn_expr->pn_type) {
628        case TOK_NUMBER:
629          Stream_write_char(f, '(');
630          output_expression(node->pn_expr, f, false);
631          Stream_write_char(f, ')');
632          break;
633        default:
634          output_expression(node->pn_expr, f, true);
635          break;
636        }
637      /*      /*
638      This may have originally been x['foo-bar'].  Because the string 'foo-bar'      This may have originally been x['foo-bar'].  Because the string 'foo-bar'
639      contains illegal characters, we have to use the subscript syntax instead of      contains illegal characters, we have to use the subscript syntax instead of
640      the dot syntax.      the dot syntax.
641      */      */
     instrument_expression(node->pn_expr, f);  
642      assert(ATOM_IS_STRING(node->pn_atom));      assert(ATOM_IS_STRING(node->pn_atom));
643      {      {
644        JSString * s = ATOM_TO_STRING(node->pn_atom);        JSString * s = ATOM_TO_STRING(node->pn_atom);
645        /* XXX - semantics changed in 1.7 */        bool must_quote;
646        if (! ATOM_KEYWORD(node->pn_atom) && js_IsIdentifier(s)) {        if (JSSTRING_LENGTH(s) == 0) {
647          Stream_write_char(f, '.');          must_quote = true;
648          print_string_atom(node->pn_atom, f);        }
649          else if (js_CheckKeyword(JSSTRING_CHARS(s), JSSTRING_LENGTH(s)) != TOK_EOF) {
650            must_quote = true;
651          }
652          else if (! js_IsIdentifier(s)) {
653            must_quote = true;
654        }        }
655        else {        else {
656            must_quote = false;
657          }
658          if (must_quote) {
659          Stream_write_char(f, '[');          Stream_write_char(f, '[');
660          print_quoted_string_atom(node->pn_atom, f);          print_quoted_string_atom(node->pn_atom, f);
661          Stream_write_char(f, ']');          Stream_write_char(f, ']');
662        }        }
663          else {
664            Stream_write_char(f, '.');
665            print_string_atom(node->pn_atom, f);
666          }
667      }      }
668      break;      break;
669    case TOK_LB:    case TOK_LB:
670      instrument_expression(node->pn_left, f);      output_expression(node->pn_left, f, false);
671      Stream_write_char(f, '[');      Stream_write_char(f, '[');
672      instrument_expression(node->pn_right, f);      output_expression(node->pn_right, f, false);
673      Stream_write_char(f, ']');      Stream_write_char(f, ']');
674      break;      break;
675    case TOK_LP:    case TOK_LP:
# Line 427  Line 683 
683        }        }
684        /* TOK_COMMA is a special case: a hole in the array */        /* TOK_COMMA is a special case: a hole in the array */
685        if (p->pn_type != TOK_COMMA) {        if (p->pn_type != TOK_COMMA) {
686          instrument_expression(p, f);          output_expression(p, f, false);
687        }        }
688      }      }
689      if (node->pn_extra == PNX_ENDCOMMA) {      if (node->pn_extra == PNX_ENDCOMMA) {
# Line 436  Line 692 
692      Stream_write_char(f, ']');      Stream_write_char(f, ']');
693      break;      break;
694    case TOK_RC:    case TOK_RC:
695        if (parenthesize_object_literals) {
696          Stream_write_char(f, '(');
697        }
698      Stream_write_char(f, '{');      Stream_write_char(f, '{');
699      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) {
700        assert(p->pn_type == TOK_COLON);        if (p->pn_type != TOK_COLON) {
701            fatal_source(file_id, p->pn_pos.begin.lineno, "unsupported node type (%d)", p->pn_type);
702          }
703        if (p != node->pn_head) {        if (p != node->pn_head) {
704          Stream_write_string(f, ", ");          Stream_write_string(f, ", ");
705        }        }
# Line 446  Line 707 
707        /* check whether this is a getter or setter */        /* check whether this is a getter or setter */
708        switch (p->pn_op) {        switch (p->pn_op) {
709        case JSOP_GETTER:        case JSOP_GETTER:
         Stream_write_string(f, "get ");  
         instrument_expression(p->pn_left, f);  
         instrument_function(p->pn_right, f, 0, FUNCTION_GETTER_OR_SETTER);  
         break;  
710        case JSOP_SETTER:        case JSOP_SETTER:
711          Stream_write_string(f, "set ");          if (p->pn_op == JSOP_GETTER) {
712          instrument_expression(p->pn_left, f);            Stream_write_string(f, "get ");
713            }
714            else {
715              Stream_write_string(f, "set ");
716            }
717            output_expression(p->pn_left, f, false);
718            Stream_write_char(f, ' ');
719            if (p->pn_right->pn_type != TOK_FUNCTION) {
720              fatal_source(file_id, p->pn_pos.begin.lineno, "expected function");
721            }
722          instrument_function(p->pn_right, f, 0, FUNCTION_GETTER_OR_SETTER);          instrument_function(p->pn_right, f, 0, FUNCTION_GETTER_OR_SETTER);
723          break;          break;
724        default:        default:
725          instrument_expression(p->pn_left, f);          output_expression(p->pn_left, f, false);
726          Stream_write_string(f, ": ");          Stream_write_string(f, ": ");
727          instrument_expression(p->pn_right, f);          output_expression(p->pn_right, f, false);
728          break;          break;
729        }        }
730      }      }
731      Stream_write_char(f, '}');      Stream_write_char(f, '}');
732        if (parenthesize_object_literals) {
733          Stream_write_char(f, ')');
734        }
735      break;      break;
736    case TOK_RP:    case TOK_RP:
737      Stream_write_char(f, '(');      Stream_write_char(f, '(');
738      instrument_expression(node->pn_kid, f);      output_expression(node->pn_kid, f, false);
739      Stream_write_char(f, ')');      Stream_write_char(f, ')');
740      break;      break;
741    case TOK_NAME:    case TOK_NAME:
742      print_string_atom(node->pn_atom, f);      print_string_atom(node->pn_atom, f);
743        if (node->pn_expr != NULL) {
744          Stream_write_string(f, " = ");
745          output_expression(node->pn_expr, f, false);
746        }
747      break;      break;
748    case TOK_STRING:    case TOK_STRING:
749      print_quoted_string_atom(node->pn_atom, f);      print_quoted_string_atom(node->pn_atom, f);
750      break;      break;
751    case TOK_OBJECT:    case TOK_REGEXP:
752      switch (node->pn_op) {      assert(node->pn_op == JSOP_REGEXP);
753      case JSOP_OBJECT:      {
754        /* I assume this is JSOP_REGEXP */        JSObject * object = node->pn_pob->object;
755        abort();        jsval result;
756        break;        js_regexp_toString(context, object, &result);
757      case JSOP_REGEXP:        print_regex(result, f);
       assert(ATOM_IS_OBJECT(node->pn_atom));  
       {  
         JSObject * object = ATOM_TO_OBJECT(node->pn_atom);  
         jsval result;  
         js_regexp_toString(context, object, 0, NULL, &result);  
         print_string_jsval(result, f);  
       }  
       break;  
     default:  
       abort();  
       break;  
758      }      }
759      break;      break;
760    case TOK_NUMBER:    case TOK_NUMBER:
# Line 503  Line 765 
765      To keep the output simple, special-case zero.      To keep the output simple, special-case zero.
766      */      */
767      if (node->pn_dval == 0.0) {      if (node->pn_dval == 0.0) {
768        Stream_write_string(f, "0");        if (signbit(node->pn_dval)) {
769            Stream_write_string(f, "-0");
770          }
771          else {
772            Stream_write_string(f, "0");
773          }
774        }
775        else if (node->pn_dval == INFINITY) {
776          Stream_write_string(f, "Number.POSITIVE_INFINITY");
777        }
778        else if (node->pn_dval == -INFINITY) {
779          Stream_write_string(f, "Number.NEGATIVE_INFINITY");
780        }
781        else if (isnan(node->pn_dval)) {
782          Stream_write_string(f, "Number.NaN");
783      }      }
784      else {      else {
785        Stream_printf(f, "%.15g", node->pn_dval);        Stream_printf(f, "%.15g", node->pn_dval);
# Line 529  Line 805 
805      }      }
806      break;      break;
807    case TOK_INSTANCEOF:    case TOK_INSTANCEOF:
808      instrument_expression(node->pn_left, f);      output_expression(node->pn_left, f, parenthesize_object_literals);
809      Stream_write_string(f, " instanceof ");      Stream_write_string(f, " instanceof ");
810      instrument_expression(node->pn_right, f);      output_expression(node->pn_right, f, false);
811      break;      break;
812    case TOK_IN:    case TOK_IN:
813      instrument_expression(node->pn_left, f);      output_expression(node->pn_left, f, false);
814      Stream_write_string(f, " in ");      Stream_write_string(f, " in ");
815      instrument_expression(node->pn_right, f);      output_expression(node->pn_right, f, false);
816      break;      break;
817    default:    case TOK_LEXICALSCOPE:
818      fatal("unsupported node type in file %s: %d", file_id, node->pn_type);      assert(node->pn_arity == PN_NAME);
819    }      assert(node->pn_expr->pn_type == TOK_LET);
820  }      assert(node->pn_expr->pn_arity == PN_BINARY);
821        Stream_write_string(f, "let(");
822  static void instrument_var_statement(JSParseNode * node, Stream * f, int indent) {      assert(node->pn_expr->pn_left->pn_type == TOK_LP);
823    assert(node->pn_arity == PN_LIST);      assert(node->pn_expr->pn_left->pn_arity == PN_LIST);
824    Stream_printf(f, "%*s", indent, "");      instrument_declarations(node->pn_expr->pn_left, f);
825    Stream_write_string(f, "var ");      Stream_write_string(f, ") ");
826    for (struct JSParseNode * p = node->pn_u.list.head; p != NULL; p = p->pn_next) {      output_expression(node->pn_expr->pn_right, f, true);
827      assert(p->pn_type == TOK_NAME);      break;
828      assert(p->pn_arity == PN_NAME);    case TOK_YIELD:
829      if (p != node->pn_head) {      assert(node->pn_arity == PN_UNARY);
830        Stream_write_string(f, ", ");      Stream_write_string(f, "yield");
831        if (node->pn_kid != NULL) {
832          Stream_write_char(f, ' ');
833          output_expression(node->pn_kid, f, true);
834      }      }
835      print_string_atom(p->pn_atom, f);      break;
836      if (p->pn_expr != NULL) {    case TOK_ARRAYCOMP:
837        Stream_write_string(f, " = ");      assert(node->pn_arity == PN_LIST);
838        instrument_expression(p->pn_expr, f);      {
839          JSParseNode * block_node;
840          switch (node->pn_count) {
841          case 1:
842            block_node = node->pn_head;
843            break;
844          case 2:
845            block_node = node->pn_head->pn_next;
846            break;
847          default:
848            abort();
849            break;
850          }
851          Stream_write_char(f, '[');
852          output_array_comprehension_or_generator_expression(block_node, f);
853          Stream_write_char(f, ']');
854      }      }
855        break;
856      case TOK_VAR:
857        assert(node->pn_arity == PN_LIST);
858        Stream_write_string(f, "var ");
859        instrument_declarations(node, f);
860        break;
861      case TOK_LET:
862        assert(node->pn_arity == PN_LIST);
863        Stream_write_string(f, "let ");
864        instrument_declarations(node, f);
865        break;
866      default:
867        fatal_source(file_id, node->pn_pos.begin.lineno, "unsupported node type (%d)", node->pn_type);
868    }    }
869  }  }
870    
871  static void output_statement(JSParseNode * node, Stream * f, int indent) {  static void output_statement(JSParseNode * node, Stream * f, int indent, bool is_jscoverage_if) {
872    switch (node->pn_type) {    switch (node->pn_type) {
873    case TOK_FUNCTION:    case TOK_FUNCTION:
874      instrument_function(node, f, indent, FUNCTION_NORMAL);      instrument_function(node, f, indent, FUNCTION_NORMAL);
875        Stream_write_char(f, '\n');
876      break;      break;
877    case TOK_LC:    case TOK_LC:
878      assert(node->pn_arity == PN_LIST);      assert(node->pn_arity == PN_LIST);
# Line 572  Line 880 
880      Stream_write_string(f, "{\n");      Stream_write_string(f, "{\n");
881  */  */
882      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) {
883        instrument_statement(p, f, indent);        instrument_statement(p, f, indent, false);
884      }      }
885  /*  /*
886      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
# Line 580  Line 888 
888  */  */
889      break;      break;
890    case TOK_IF:    case TOK_IF:
891      {
892      assert(node->pn_arity == PN_TERNARY);      assert(node->pn_arity == PN_TERNARY);
893    
894        uint16_t line = node->pn_pos.begin.lineno;
895        if (! is_jscoverage_if) {
896          if (line > num_lines) {
897            fatal("file %s contains more than 65,535 lines", file_id);
898          }
899          if (line >= 2 && exclusive_directives[line - 2]) {
900            is_jscoverage_if = true;
901          }
902        }
903    
904      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
905      Stream_write_string(f, "if (");      Stream_write_string(f, "if (");
906      instrument_expression(node->pn_kid1, f);      output_expression(node->pn_kid1, f, false);
907      Stream_write_string(f, ") {\n");      Stream_write_string(f, ") {\n");
908      instrument_statement(node->pn_kid2, f, indent + 2);      if (is_jscoverage_if && node->pn_kid3) {
909          uint16_t else_start = node->pn_kid3->pn_pos.begin.lineno;
910          uint16_t else_end = node->pn_kid3->pn_pos.end.lineno + 1;
911          Stream_printf(f, "%*s", indent + 2, "");
912          Stream_printf(f, "_$jscoverage['%s'].conditionals[%d] = %d;\n", file_id, else_start, else_end);
913        }
914        instrument_statement(node->pn_kid2, f, indent + 2, false);
915      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
916      Stream_write_string(f, "}\n");      Stream_write_string(f, "}\n");
917      if (node->pn_kid3) {  
918        if (node->pn_kid3 || is_jscoverage_if) {
919        Stream_printf(f, "%*s", indent, "");        Stream_printf(f, "%*s", indent, "");
920        Stream_write_string(f, "else {\n");        Stream_write_string(f, "else {\n");
921        instrument_statement(node->pn_kid3, f, indent + 2);  
922          if (is_jscoverage_if) {
923            uint16_t if_start = node->pn_kid2->pn_pos.begin.lineno + 1;
924            uint16_t if_end = node->pn_kid2->pn_pos.end.lineno + 1;
925            Stream_printf(f, "%*s", indent + 2, "");
926            Stream_printf(f, "_$jscoverage['%s'].conditionals[%d] = %d;\n", file_id, if_start, if_end);
927          }
928    
929          if (node->pn_kid3) {
930            instrument_statement(node->pn_kid3, f, indent + 2, is_jscoverage_if);
931          }
932    
933        Stream_printf(f, "%*s", indent, "");        Stream_printf(f, "%*s", indent, "");
934        Stream_write_string(f, "}\n");        Stream_write_string(f, "}\n");
935      }      }
936    
937      break;      break;
938      }
939    case TOK_SWITCH:    case TOK_SWITCH:
940      assert(node->pn_arity == PN_BINARY);      assert(node->pn_arity == PN_BINARY);
941      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
942      Stream_write_string(f, "switch (");      Stream_write_string(f, "switch (");
943      instrument_expression(node->pn_left, f);      output_expression(node->pn_left, f, false);
944      Stream_write_string(f, ") {\n");      Stream_write_string(f, ") {\n");
945      for (struct JSParseNode * p = node->pn_right->pn_head; p != NULL; p = p->pn_next) {      {
946        Stream_printf(f, "%*s", indent, "");        JSParseNode * list = node->pn_right;
947        switch (p->pn_type) {        if (list->pn_type == TOK_LEXICALSCOPE) {
948        case TOK_CASE:          list = list->pn_expr;
949          Stream_write_string(f, "case ");        }
950          instrument_expression(p->pn_left, f);        for (struct JSParseNode * p = list->pn_head; p != NULL; p = p->pn_next) {
951          Stream_write_string(f, ":\n");          Stream_printf(f, "%*s", indent, "");
952          break;          switch (p->pn_type) {
953        case TOK_DEFAULT:          case TOK_CASE:
954          Stream_write_string(f, "default:\n");            Stream_write_string(f, "case ");
955          break;            output_expression(p->pn_left, f, false);
956        default:            Stream_write_string(f, ":\n");
957          abort();            break;
958          break;          case TOK_DEFAULT:
959              Stream_write_string(f, "default:\n");
960              break;
961            default:
962              abort();
963              break;
964            }
965            instrument_statement(p->pn_right, f, indent + 2, false);
966        }        }
       instrument_statement(p->pn_right, f, indent + 2);  
967      }      }
968      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
969      Stream_write_string(f, "}\n");      Stream_write_string(f, "}\n");
# Line 630  Line 976 
976      assert(node->pn_arity == PN_BINARY);      assert(node->pn_arity == PN_BINARY);
977      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
978      Stream_write_string(f, "while (");      Stream_write_string(f, "while (");
979      instrument_expression(node->pn_left, f);      output_expression(node->pn_left, f, false);
980      Stream_write_string(f, ") {\n");      Stream_write_string(f, ") {\n");
981      instrument_statement(node->pn_right, f, indent + 2);      instrument_statement(node->pn_right, f, indent + 2, false);
982      Stream_write_string(f, "}\n");      Stream_write_string(f, "}\n");
983      break;      break;
984    case TOK_DO:    case TOK_DO:
985      assert(node->pn_arity == PN_BINARY);      assert(node->pn_arity == PN_BINARY);
986      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
987      Stream_write_string(f, "do {\n");      Stream_write_string(f, "do {\n");
988      instrument_statement(node->pn_left, f, indent + 2);      instrument_statement(node->pn_left, f, indent + 2, false);
989      Stream_write_string(f, "}\n");      Stream_write_string(f, "}\n");
990      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
991      Stream_write_string(f, "while (");      Stream_write_string(f, "while (");
992      instrument_expression(node->pn_right, f);      output_expression(node->pn_right, f, false);
993      Stream_write_string(f, ");\n");      Stream_write_string(f, ");\n");
994      break;      break;
995    case TOK_FOR:    case TOK_FOR:
996      assert(node->pn_arity == PN_BINARY);      assert(node->pn_arity == PN_BINARY);
997      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
     Stream_write_string(f, "for (");  
998      switch (node->pn_left->pn_type) {      switch (node->pn_left->pn_type) {
999      case TOK_IN:      case TOK_IN:
1000        /* for/in */        /* for/in */
1001        assert(node->pn_left->pn_arity == PN_BINARY);        assert(node->pn_left->pn_arity == PN_BINARY);
1002        switch (node->pn_left->pn_left->pn_type) {        output_for_in(node, f);
       case TOK_VAR:  
         instrument_var_statement(node->pn_left->pn_left, f, 0);  
         break;  
       case TOK_NAME:  
         instrument_expression(node->pn_left->pn_left, f);  
         break;  
       default:  
         /* this is undocumented: for (x.value in y) */  
         instrument_expression(node->pn_left->pn_left, f);  
         break;  
 /*  
       default:  
         fprintf(stderr, "unexpected node type: %d\n", node->pn_left->pn_left->pn_type);  
         abort();  
         break;  
 */  
       }  
       Stream_write_string(f, " in ");  
       instrument_expression(node->pn_left->pn_right, f);  
1003        break;        break;
1004      case TOK_RESERVED:      case TOK_RESERVED:
1005        /* for (;;) */        /* for (;;) */
1006        assert(node->pn_left->pn_arity == PN_TERNARY);        assert(node->pn_left->pn_arity == PN_TERNARY);
1007          Stream_write_string(f, "for (");
1008        if (node->pn_left->pn_kid1) {        if (node->pn_left->pn_kid1) {
1009          if (node->pn_left->pn_kid1->pn_type == TOK_VAR) {          output_expression(node->pn_left->pn_kid1, f, false);
           instrument_var_statement(node->pn_left->pn_kid1, f, 0);  
         }  
         else {  
           instrument_expression(node->pn_left->pn_kid1, f);  
         }  
1010        }        }
1011        Stream_write_string(f, ";");        Stream_write_string(f, ";");
1012        if (node->pn_left->pn_kid2) {        if (node->pn_left->pn_kid2) {
1013          Stream_write_char(f, ' ');          Stream_write_char(f, ' ');
1014          instrument_expression(node->pn_left->pn_kid2, f);          output_expression(node->pn_left->pn_kid2, f, false);
1015        }        }
1016        Stream_write_string(f, ";");        Stream_write_string(f, ";");
1017        if (node->pn_left->pn_kid3) {        if (node->pn_left->pn_kid3) {
1018          Stream_write_char(f, ' ');          Stream_write_char(f, ' ');
1019          instrument_expression(node->pn_left->pn_kid3, f);          output_expression(node->pn_left->pn_kid3, f, false);
1020        }        }
1021          Stream_write_char(f, ')');
1022        break;        break;
1023      default:      default:
1024        abort();        abort();
1025        break;        break;
1026      }      }
1027      Stream_write_string(f, ") {\n");      Stream_write_string(f, " {\n");
1028      instrument_statement(node->pn_right, f, indent + 2);      instrument_statement(node->pn_right, f, indent + 2, false);
1029      Stream_write_string(f, "}\n");      Stream_write_string(f, "}\n");
1030      break;      break;
1031    case TOK_THROW:    case TOK_THROW:
1032      assert(node->pn_arity == PN_UNARY);      assert(node->pn_arity == PN_UNARY);
1033      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
1034      Stream_write_string(f, "throw ");      Stream_write_string(f, "throw ");
1035      instrument_expression(node->pn_u.unary.kid, f);      output_expression(node->pn_u.unary.kid, f, false);
1036      Stream_write_string(f, ";\n");      Stream_write_string(f, ";\n");
1037      break;      break;
1038    case TOK_TRY:    case TOK_TRY:
1039      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
1040      Stream_write_string(f, "try {\n");      Stream_write_string(f, "try {\n");
1041      instrument_statement(node->pn_kid1, f, indent + 2);      instrument_statement(node->pn_kid1, f, indent + 2, false);
1042      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
1043      Stream_write_string(f, "}\n");      Stream_write_string(f, "}\n");
1044      {      if (node->pn_kid2) {
1045        for (JSParseNode * catch = node->pn_kid2; catch != NULL; catch = catch->pn_kid2) {        assert(node->pn_kid2->pn_type == TOK_RESERVED);
1046          for (JSParseNode * scope = node->pn_kid2->pn_head; scope != NULL; scope = scope->pn_next) {
1047            assert(scope->pn_type == TOK_LEXICALSCOPE);
1048            JSParseNode * catch = scope->pn_expr;
1049          assert(catch->pn_type == TOK_CATCH);          assert(catch->pn_type == TOK_CATCH);
1050          Stream_printf(f, "%*s", indent, "");          Stream_printf(f, "%*s", indent, "");
1051          Stream_write_string(f, "catch (");          Stream_write_string(f, "catch (");
1052          assert(catch->pn_kid1->pn_arity == PN_NAME);          output_expression(catch->pn_kid1, f, false);
1053          print_string_atom(catch->pn_kid1->pn_atom, f);          if (catch->pn_kid2) {
         if (catch->pn_kid1->pn_expr) {  
1054            Stream_write_string(f, " if ");            Stream_write_string(f, " if ");
1055            instrument_expression(catch->pn_kid1->pn_expr, f);            output_expression(catch->pn_kid2, f, false);
1056          }          }
1057          Stream_write_string(f, ") {\n");          Stream_write_string(f, ") {\n");
1058          instrument_statement(catch->pn_kid3, f, indent + 2);          instrument_statement(catch->pn_kid3, f, indent + 2, false);
1059          Stream_printf(f, "%*s", indent, "");          Stream_printf(f, "%*s", indent, "");
1060          Stream_write_string(f, "}\n");          Stream_write_string(f, "}\n");
1061        }        }
# Line 738  Line 1063 
1063      if (node->pn_kid3) {      if (node->pn_kid3) {
1064        Stream_printf(f, "%*s", indent, "");        Stream_printf(f, "%*s", indent, "");
1065        Stream_write_string(f, "finally {\n");        Stream_write_string(f, "finally {\n");
1066        instrument_statement(node->pn_kid3, f, indent + 2);        instrument_statement(node->pn_kid3, f, indent + 2, false);
1067        Stream_printf(f, "%*s", indent, "");        Stream_printf(f, "%*s", indent, "");
1068        Stream_write_string(f, "}\n");        Stream_write_string(f, "}\n");
1069      }      }
# Line 762  Line 1087 
1087      assert(node->pn_arity == PN_BINARY);      assert(node->pn_arity == PN_BINARY);
1088      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
1089      Stream_write_string(f, "with (");      Stream_write_string(f, "with (");
1090      instrument_expression(node->pn_left, f);      output_expression(node->pn_left, f, false);
1091      Stream_write_string(f, ") {\n");      Stream_write_string(f, ") {\n");
1092      instrument_statement(node->pn_right, f, indent + 2);      instrument_statement(node->pn_right, f, indent + 2, false);
1093      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
1094      Stream_write_string(f, "}\n");      Stream_write_string(f, "}\n");
1095      break;      break;
1096    case TOK_VAR:    case TOK_VAR:
1097      instrument_var_statement(node, f, indent);      Stream_printf(f, "%*s", indent, "");
1098        output_expression(node, f, false);
1099      Stream_write_string(f, ";\n");      Stream_write_string(f, ";\n");
1100      break;      break;
1101    case TOK_RETURN:    case TOK_RETURN:
# Line 778  Line 1104 
1104      Stream_write_string(f, "return");      Stream_write_string(f, "return");
1105      if (node->pn_kid != NULL) {      if (node->pn_kid != NULL) {
1106        Stream_write_char(f, ' ');        Stream_write_char(f, ' ');
1107        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, true);
1108      }      }
1109      Stream_write_string(f, ";\n");      Stream_write_string(f, ";\n");
1110      break;      break;
# Line 786  Line 1112 
1112      assert(node->pn_arity == PN_UNARY);      assert(node->pn_arity == PN_UNARY);
1113      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
1114      if (node->pn_kid != NULL) {      if (node->pn_kid != NULL) {
1115        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, true);
1116      }      }
1117      Stream_write_string(f, ";\n");      Stream_write_string(f, ";\n");
1118      break;      break;
1119    case TOK_COLON:    case TOK_COLON:
1120      {
1121      assert(node->pn_arity == PN_NAME);      assert(node->pn_arity == PN_NAME);
     /*  
     This one is tricky: can't output instrumentation between the label and the  
     statement it's supposed to label ...  
     */  
1122      Stream_printf(f, "%*s", indent < 2? 0: indent - 2, "");      Stream_printf(f, "%*s", indent < 2? 0: indent - 2, "");
1123      print_string_atom(node->pn_atom, f);      print_string_atom(node->pn_atom, f);
1124      Stream_write_string(f, ":\n");      Stream_write_string(f, ":\n");
1125      /*      JSParseNode * labelled = node->pn_expr;
1126      ... use output_statement instead of instrument_statement.      if (labelled->pn_type == TOK_LEXICALSCOPE) {
1127      */        labelled = labelled->pn_expr;
1128      output_statement(node->pn_expr, f, indent);      }
1129        if (labelled->pn_type == TOK_LC) {
1130          /* labelled block */
1131          Stream_printf(f, "%*s", indent, "");
1132          Stream_write_string(f, "{\n");
1133          instrument_statement(labelled, f, indent + 2, false);
1134          Stream_printf(f, "%*s", indent, "");
1135          Stream_write_string(f, "}\n");
1136        }
1137        else {
1138          /*
1139          This one is tricky: can't output instrumentation between the label and the
1140          statement it's supposed to label, so use output_statement instead of
1141          instrument_statement.
1142          */
1143          output_statement(labelled, f, indent, false);
1144        }
1145        break;
1146      }
1147      case TOK_LEXICALSCOPE:
1148        /* let statement */
1149        assert(node->pn_arity == PN_NAME);
1150        switch (node->pn_expr->pn_type) {
1151        case TOK_LET:
1152          /* let statement */
1153          assert(node->pn_expr->pn_arity == PN_BINARY);
1154          instrument_statement(node->pn_expr, f, indent, false);
1155          break;
1156        case TOK_LC:
1157          /* block */
1158          Stream_printf(f, "%*s", indent, "");
1159          Stream_write_string(f, "{\n");
1160          instrument_statement(node->pn_expr, f, indent + 2, false);
1161          Stream_printf(f, "%*s", indent, "");
1162          Stream_write_string(f, "}\n");
1163          break;
1164        case TOK_FOR:
1165          instrument_statement(node->pn_expr, f, indent, false);
1166          break;
1167        default:
1168          abort();
1169          break;
1170        }
1171        break;
1172      case TOK_LET:
1173        switch (node->pn_arity) {
1174        case PN_BINARY:
1175          /* let statement */
1176          Stream_printf(f, "%*s", indent, "");
1177          Stream_write_string(f, "let (");
1178          assert(node->pn_left->pn_type == TOK_LP);
1179          assert(node->pn_left->pn_arity == PN_LIST);
1180          instrument_declarations(node->pn_left, f);
1181          Stream_write_string(f, ") {\n");
1182          instrument_statement(node->pn_right, f, indent + 2, false);
1183          Stream_printf(f, "%*s", indent, "");
1184          Stream_write_string(f, "}\n");
1185          break;
1186        case PN_LIST:
1187          /* let definition */
1188          Stream_printf(f, "%*s", indent, "");
1189          output_expression(node, f, false);
1190          Stream_write_string(f, ";\n");
1191          break;
1192        default:
1193          abort();
1194          break;
1195        }
1196        break;
1197      case TOK_DEBUGGER:
1198        Stream_printf(f, "%*s", indent, "");
1199        Stream_write_string(f, "debugger;\n");
1200      break;      break;
1201    default:    default:
1202      fatal("unsupported node type in file %s: %d", file_id, node->pn_type);      fatal_source(file_id, node->pn_pos.begin.lineno, "unsupported node type (%d)", node->pn_type);
1203    }    }
1204  }  }
1205    
# Line 814  Line 1208 
1208  TOK_FUNCTION is handled as a statement and as an expression.  TOK_FUNCTION is handled as a statement and as an expression.
1209  TOK_EXPORT, TOK_IMPORT are not handled.  TOK_EXPORT, TOK_IMPORT are not handled.
1210  */  */
1211  static void instrument_statement(JSParseNode * node, Stream * f, int indent) {  static void instrument_statement(JSParseNode * node, Stream * f, int indent, bool is_jscoverage_if) {
1212    if (node->pn_type != TOK_LC) {    if (node->pn_type != TOK_LC && node->pn_type != TOK_LEXICALSCOPE) {
1213      int line = node->pn_pos.begin.lineno;      uint16_t line = node->pn_pos.begin.lineno;
1214        if (line > num_lines) {
1215          fatal("file %s contains more than 65,535 lines", file_id);
1216        }
1217    
1218      /* the root node has line number 0 */      /* the root node has line number 0 */
1219      if (line != 0) {      if (line != 0) {
1220        Stream_printf(f, "%*s", indent, "");        Stream_printf(f, "%*s", indent, "");
# Line 824  Line 1222 
1222        lines[line - 1] = 1;        lines[line - 1] = 1;
1223      }      }
1224    }    }
1225    output_statement(node, f, indent);    output_statement(node, f, indent, is_jscoverage_if);
1226  }  }
1227    
1228  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) {
1229    file_id = id;    const jschar * characters_end = characters + line_end;
1230      const jschar * cp = characters + line_start;
1231    /* scan the javascript */    const char * bp = prefix;
1232    size_t input_length = input->length;    for (;;) {
1233    jschar * base = js_InflateString(context, (char *) input->data, &input_length);      if (*bp == '\0') {
1234    if (base == NULL) {        return true;
1235      fatal("out of memory");      }
1236        else if (cp == characters_end) {
1237          return false;
1238        }
1239        else if (*cp != *bp) {
1240          return false;
1241        }
1242        bp++;
1243        cp++;
1244    }    }
1245    JSTokenStream * token_stream = js_NewTokenStream(context, base, input_length, NULL, 1, NULL);  }
1246    if (token_stream == NULL) {  
1247      fatal("cannot create token stream from file: %s", file_id);  static bool characters_are_white_space(const jschar * characters, size_t line_start, size_t line_end) {
1248      /* XXX - other Unicode space */
1249      const jschar * end = characters + line_end;
1250      for (const jschar * p = characters + line_start; p < end; p++) {
1251        jschar c = *p;
1252        if (c == 0x9 || c == 0xB || c == 0xC || c == 0x20 || c == 0xA0) {
1253          continue;
1254        }
1255        else {
1256          return false;
1257        }
1258    }    }
1259      return true;
1260    }
1261    
1262    static void error_reporter(JSContext * context, const char * message, JSErrorReport * report) {
1263      warn_source(file_id, report->lineno, "%s", message);
1264    }
1265    
1266    void jscoverage_instrument_js(const char * id, const uint16_t * characters, size_t num_characters, Stream * output) {
1267      file_id = id;
1268    
1269    /* parse the javascript */    /* parse the javascript */
1270    JSParseNode * node = js_ParseTokenStream(context, global, token_stream);    JSParseContext parse_context;
1271      if (! js_InitParseContext(context, &parse_context, NULL, NULL, characters, num_characters, NULL, NULL, 1)) {
1272        fatal("cannot create token stream from file %s", file_id);
1273      }
1274      JSErrorReporter old_error_reporter = JS_SetErrorReporter(context, error_reporter);
1275      JSParseNode * node = js_ParseScript(context, global, &parse_context);
1276    if (node == NULL) {    if (node == NULL) {
1277      fatal("parse error in file: %s", file_id);      js_ReportUncaughtException(context);
1278        fatal("parse error in file %s", file_id);
1279    }    }
1280    int num_lines = node->pn_pos.end.lineno;    JS_SetErrorReporter(context, old_error_reporter);
1281      num_lines = node->pn_pos.end.lineno;
1282    lines = xmalloc(num_lines);    lines = xmalloc(num_lines);
1283    for (int i = 0; i < num_lines; i++) {    for (unsigned int i = 0; i < num_lines; i++) {
1284      lines[i] = 0;      lines[i] = 0;
1285    }    }
1286    
1287      /* search code for conditionals */
1288      exclusive_directives = xnew(bool, num_lines);
1289      for (unsigned int i = 0; i < num_lines; i++) {
1290        exclusive_directives[i] = false;
1291      }
1292    
1293      bool has_conditionals = false;
1294      struct IfDirective * if_directives = NULL;
1295      size_t line_number = 0;
1296      size_t i = 0;
1297      while (i < num_characters) {
1298        if (line_number == UINT16_MAX) {
1299          fatal("file %s contains more than 65,535 lines", file_id);
1300        }
1301        line_number++;
1302        size_t line_start = i;
1303        jschar c;
1304        bool done = false;
1305        while (! done && i < num_characters) {
1306          c = characters[i];
1307          switch (c) {
1308          case '\r':
1309          case '\n':
1310          case 0x2028:
1311          case 0x2029:
1312            done = true;
1313            break;
1314          default:
1315            i++;
1316          }
1317        }
1318        size_t line_end = i;
1319        if (i < num_characters) {
1320          i++;
1321          if (c == '\r' && i < num_characters && characters[i] == '\n') {
1322            i++;
1323          }
1324        }
1325    
1326        if (characters_start_with(characters, line_start, line_end, "//#JSCOVERAGE_IF")) {
1327          has_conditionals = true;
1328    
1329          if (characters_are_white_space(characters, line_start + 16, line_end)) {
1330            exclusive_directives[line_number - 1] = true;
1331          }
1332          else {
1333            struct IfDirective * if_directive = xnew(struct IfDirective, 1);
1334            if_directive->condition_start = characters + line_start + 16;
1335            if_directive->condition_end = characters + line_end;
1336            if_directive->start_line = line_number;
1337            if_directive->end_line = 0;
1338            if_directive->next = if_directives;
1339            if_directives = if_directive;
1340          }
1341        }
1342        else if (characters_start_with(characters, line_start, line_end, "//#JSCOVERAGE_ENDIF")) {
1343          for (struct IfDirective * p = if_directives; p != NULL; p = p->next) {
1344            if (p->end_line == 0) {
1345              p->end_line = line_number;
1346              break;
1347            }
1348          }
1349        }
1350      }
1351    
1352    /*    /*
1353    An instrumented JavaScript file has 3 sections:    An instrumented JavaScript file has 4 sections:
1354    1. initialization    1. initialization
1355    2. instrumented source code    2. instrumented source code
1356    3. original source code    3. conditionals
1357      4. original source code
1358    */    */
1359    
1360    Stream * instrumented = Stream_new(0);    Stream * instrumented = Stream_new(0);
1361    instrument_statement(node, instrumented, 0);    instrument_statement(node, instrumented, 0, false);
1362      js_FinishParseContext(context, &parse_context);
1363    
1364    /* write line number info to the output */    /* write line number info to the output */
1365    Stream_write_string(output, "/* automatically generated by JSCoverage - do not edit */\n");    Stream_write_string(output, "/* automatically generated by JSCoverage - do not edit */\n");
1366    Stream_write_string(output, "if (! top._$jscoverage) {\n  top._$jscoverage = {};\n}\n");    if (jscoverage_mozilla) {
1367    Stream_write_string(output, "var _$jscoverage = top._$jscoverage;\n");      Stream_write_string(output, "try {\n");
1368        Stream_write_string(output, "  Components.utils.import('resource://gre/modules/jscoverage.jsm');\n");
1369        Stream_printf(output, "  dump('%s: successfully imported jscoverage module\\n');\n", id);
1370        Stream_write_string(output, "}\n");
1371        Stream_write_string(output, "catch (e) {\n");
1372        Stream_write_string(output, "  _$jscoverage = {};\n");
1373        Stream_printf(output, "  dump('%s: failed to import jscoverage module - coverage not available for this file\\n');\n", id);
1374        Stream_write_string(output, "}\n");
1375      }
1376      else {
1377        Stream_write_string(output, "if (! top._$jscoverage) {\n  top._$jscoverage = {};\n}\n");
1378        Stream_write_string(output, "var _$jscoverage = top._$jscoverage;\n");
1379      }
1380    Stream_printf(output, "if (! _$jscoverage['%s']) {\n", file_id);    Stream_printf(output, "if (! _$jscoverage['%s']) {\n", file_id);
1381    Stream_printf(output, "  _$jscoverage['%s'] = [];\n", file_id);    Stream_printf(output, "  _$jscoverage['%s'] = [];\n", file_id);
1382    for (int i = 0; i < num_lines; i++) {    for (int i = 0; i < num_lines; i++) {
# Line 876  Line 1387 
1387    Stream_write_string(output, "}\n");    Stream_write_string(output, "}\n");
1388    free(lines);    free(lines);
1389    lines = NULL;    lines = NULL;
1390      free(exclusive_directives);
1391      exclusive_directives = NULL;
1392    
1393      /* conditionals */
1394      if (has_conditionals) {
1395        Stream_printf(output, "_$jscoverage['%s'].conditionals = [];\n", file_id);
1396      }
1397    
1398    /* copy the instrumented source code to the output */    /* copy the instrumented source code to the output */
1399    Stream_write(output, instrumented->data, instrumented->length);    Stream_write(output, instrumented->data, instrumented->length);
1400    Stream_write_char(output, '\n');  
1401      /* conditionals */
1402      for (struct IfDirective * if_directive = if_directives; if_directive != NULL; if_directive = if_directive->next) {
1403        Stream_write_string(output, "if (!(");
1404        print_javascript(if_directive->condition_start, if_directive->condition_end - if_directive->condition_start, output);
1405        Stream_write_string(output, ")) {\n");
1406        Stream_printf(output, "  _$jscoverage['%s'].conditionals[%d] = %d;\n", file_id, if_directive->start_line, if_directive->end_line);
1407        Stream_write_string(output, "}\n");
1408      }
1409    
1410      /* free */
1411      while (if_directives != NULL) {
1412        struct IfDirective * if_directive = if_directives;
1413        if_directives = if_directives->next;
1414        free(if_directive);
1415      }
1416    
1417    /* copy the original source to the output */    /* copy the original source to the output */
1418    size_t i = 0;    Stream_printf(output, "_$jscoverage['%s'].source = ", file_id);
1419    while (i < input_length) {    jscoverage_write_source(id, characters, num_characters, output);
1420      Stream_write_string(output, "// ");    Stream_printf(output, ";\n");
     size_t line_start = i;  
     while (i < input_length && base[i] != '\r' && base[i] != '\n') {  
       i++;  
     }  
1421    
1422      size_t line_end = i;    Stream_delete(instrumented);
1423      if (i < input_length) {  
1424        if (base[i] == '\r') {    file_id = NULL;
1425          line_end = i;  }
1426    
1427    void jscoverage_write_source(const char * id, const jschar * characters, size_t num_characters, Stream * output) {
1428      Stream_write_string(output, "[");
1429      if (jscoverage_highlight) {
1430        Stream * highlighted_stream = Stream_new(num_characters);
1431        jscoverage_highlight_js(context, id, characters, num_characters, highlighted_stream);
1432        size_t i = 0;
1433        while (i < highlighted_stream->length) {
1434          if (i > 0) {
1435            Stream_write_char(output, ',');
1436          }
1437    
1438          Stream_write_char(output, '"');
1439          bool done = false;
1440          while (! done) {
1441            char c = highlighted_stream->data[i];
1442            switch (c) {
1443            case 0x8:
1444              /* backspace */
1445              Stream_write_string(output, "\\b");
1446              break;
1447            case 0x9:
1448              /* horizontal tab */
1449              Stream_write_string(output, "\\t");
1450              break;
1451            case 0xa:
1452              /* line feed (new line) */
1453              done = true;
1454              break;
1455            /* IE doesn't support this */
1456            /*
1457            case 0xb:
1458              Stream_write_string(output, "\\v");
1459              break;
1460            */
1461            case 0xc:
1462              /* form feed */
1463              Stream_write_string(output, "\\f");
1464              break;
1465            case 0xd:
1466              /* carriage return */
1467              done = true;
1468              if (i + 1 < highlighted_stream->length && highlighted_stream->data[i + 1] == '\n') {
1469                i++;
1470              }
1471              break;
1472            case '"':
1473              Stream_write_string(output, "\\\"");
1474              break;
1475            case '\\':
1476              Stream_write_string(output, "\\\\");
1477              break;
1478            default:
1479              Stream_write_char(output, c);
1480              break;
1481            }
1482          i++;          i++;
1483          if (i < input_length && base[i] == '\n') {          if (i >= highlighted_stream->length) {
1484            i++;            done = true;
1485          }          }
1486        }        }
1487        else if (base[i] == '\n') {        Stream_write_char(output, '"');
1488          line_end = i;      }
1489        Stream_delete(highlighted_stream);
1490      }
1491      else {
1492        size_t i = 0;
1493        while (i < num_characters) {
1494          if (i > 0) {
1495            Stream_write_char(output, ',');
1496          }
1497    
1498          Stream_write_char(output, '"');
1499          bool done = false;
1500          while (! done) {
1501            jschar c = characters[i];
1502            switch (c) {
1503            case 0x8:
1504              /* backspace */
1505              Stream_write_string(output, "\\b");
1506              break;
1507            case 0x9:
1508              /* horizontal tab */
1509              Stream_write_string(output, "\\t");
1510              break;
1511            case 0xa:
1512              /* line feed (new line) */
1513              done = true;
1514              break;
1515            /* IE doesn't support this */
1516            /*
1517            case 0xb:
1518              Stream_write_string(output, "\\v");
1519              break;
1520            */
1521            case 0xc:
1522              /* form feed */
1523              Stream_write_string(output, "\\f");
1524              break;
1525            case 0xd:
1526              /* carriage return */
1527              done = true;
1528              if (i + 1 < num_characters && characters[i + 1] == '\n') {
1529                i++;
1530              }
1531              break;
1532            case '"':
1533              Stream_write_string(output, "\\\"");
1534              break;
1535            case '\\':
1536              Stream_write_string(output, "\\\\");
1537              break;
1538            case '&':
1539              Stream_write_string(output, "&amp;");
1540              break;
1541            case '<':
1542              Stream_write_string(output, "&lt;");
1543              break;
1544            case '>':
1545              Stream_write_string(output, "&gt;");
1546              break;
1547            case 0x2028:
1548            case 0x2029:
1549              done = true;
1550              break;
1551            default:
1552              if (32 <= c && c <= 126) {
1553                Stream_write_char(output, c);
1554              }
1555              else {
1556                Stream_printf(output, "&#%d;", c);
1557              }
1558              break;
1559            }
1560          i++;          i++;
1561            if (i >= num_characters) {
1562              done = true;
1563            }
1564        }        }
1565        else {        Stream_write_char(output, '"');
         abort();  
       }  
1566      }      }
   
     char * line = js_DeflateString(context, base + line_start, line_end - line_start);  
     Stream_write_string(output, line);  
     Stream_write_char(output, '\n');  
     JS_free(context, line);  
1567    }    }
1568      Stream_write_string(output, "]");
   Stream_delete(instrumented);  
   
   JS_free(context, base);  
   
   file_id = NULL;  
1569  }  }
1570    
1571  void jscoverage_copy_resources(const char * destination_directory) {  void jscoverage_copy_resources(const char * destination_directory) {
1572    copy_resource("jscoverage.html", destination_directory);    copy_resource("jscoverage.html", destination_directory);
1573    copy_resource("jscoverage.css", destination_directory);    copy_resource("jscoverage.css", destination_directory);
1574    copy_resource("jscoverage.js", destination_directory);    copy_resource("jscoverage.js", destination_directory);
1575      copy_resource("jscoverage-ie.css", destination_directory);
1576    copy_resource("jscoverage-throbber.gif", destination_directory);    copy_resource("jscoverage-throbber.gif", destination_directory);
1577    copy_resource("jscoverage-sh_main.js", destination_directory);    copy_resource("jscoverage-highlight.css", destination_directory);
   copy_resource("jscoverage-sh_javascript.js", destination_directory);  
   copy_resource("jscoverage-sh_nedit.css", destination_directory);  
1578  }  }
1579    
1580  /*  /*
# Line 963  Line 1609 
1609    JS_HashTableDestroy(coverage->coverage_table);    JS_HashTableDestroy(coverage->coverage_table);
1610    struct FileCoverageList * p = coverage->coverage_list;    struct FileCoverageList * p = coverage->coverage_list;
1611    while (p != NULL) {    while (p != NULL) {
1612      free(p->file_coverage->lines);      free(p->file_coverage->coverage_lines);
1613      free(p->file_coverage->source);      if (p->file_coverage->source_lines != NULL) {
1614          for (uint32 i = 0; i < p->file_coverage->num_source_lines; i++) {
1615            free(p->file_coverage->source_lines[i]);
1616          }
1617          free(p->file_coverage->source_lines);
1618        }
1619      free(p->file_coverage->id);      free(p->file_coverage->id);
1620      free(p->file_coverage);      free(p->file_coverage);
1621      struct FileCoverageList * q = p;      struct FileCoverageList * q = p;
# Line 993  Line 1644 
1644  }  }
1645    
1646  int jscoverage_parse_json(Coverage * coverage, const uint8_t * json, size_t length) {  int jscoverage_parse_json(Coverage * coverage, const uint8_t * json, size_t length) {
1647      int result = 0;
1648    
1649    jschar * base = js_InflateString(context, (char *) json, &length);    jschar * base = js_InflateString(context, (char *) json, &length);
1650    if (base == NULL) {    if (base == NULL) {
1651      fatal("out of memory");      fatal("out of memory");
# Line 1005  Line 1658 
1658    
1659    JS_free(context, base);    JS_free(context, base);
1660    
1661    JSTokenStream * token_stream = js_NewTokenStream(context, parenthesized_json, length + 2, NULL, 1, NULL);    JSParseContext parse_context;
1662    if (token_stream == NULL) {    if (! js_InitParseContext(context, &parse_context, NULL, NULL, parenthesized_json, length + 2, NULL, NULL, 1)) {
1663      fatal("cannot create token stream");      free(parenthesized_json);
1664        return -1;
1665    }    }
1666      JSParseNode * root = js_ParseScript(context, global, &parse_context);
   JSParseNode * root = js_ParseTokenStream(context, global, token_stream);  
1667    free(parenthesized_json);    free(parenthesized_json);
1668    if (root == NULL) {    if (root == NULL) {
1669      return -1;      result = -1;
1670        goto done;
1671    }    }
1672    
1673    /* root node must be TOK_LC */    /* root node must be TOK_LC */
1674    if (root->pn_type != TOK_LC) {    if (root->pn_type != TOK_LC) {
1675      return -1;      result = -1;
1676        goto done;
1677    }    }
1678    JSParseNode * semi = root->pn_u.list.head;    JSParseNode * semi = root->pn_u.list.head;
1679    
1680    /* the list must be TOK_SEMI and it must contain only one element */    /* the list must be TOK_SEMI and it must contain only one element */
1681    if (semi->pn_type != TOK_SEMI || semi->pn_next != NULL) {    if (semi->pn_type != TOK_SEMI || semi->pn_next != NULL) {
1682      return -1;      result = -1;
1683        goto done;
1684    }    }
1685    JSParseNode * parenthesized = semi->pn_kid;    JSParseNode * parenthesized = semi->pn_kid;
1686    
1687    /* this must be a parenthesized expression */    /* this must be a parenthesized expression */
1688    if (parenthesized->pn_type != TOK_RP) {    if (parenthesized->pn_type != TOK_RP) {
1689      return -1;      result = -1;
1690        goto done;
1691    }    }
1692    JSParseNode * object = parenthesized->pn_kid;    JSParseNode * object = parenthesized->pn_kid;
1693    
1694    /* this must be an object literal */    /* this must be an object literal */
1695    if (object->pn_type != TOK_RC) {    if (object->pn_type != TOK_RC) {
1696      return -1;      result = -1;
1697        goto done;
1698    }    }
1699    
1700    for (JSParseNode * p = object->pn_head; p != NULL; p = p->pn_next) {    for (JSParseNode * p = object->pn_head; p != NULL; p = p->pn_next) {
1701      /* every element of this list must be TOK_COLON */      /* every element of this list must be TOK_COLON */
1702      if (p->pn_type != TOK_COLON) {      if (p->pn_type != TOK_COLON) {
1703        return -1;        result = -1;
1704          goto done;
1705      }      }
1706    
1707      /* the key must be a string representing the file */      /* the key must be a string representing the file */
1708      JSParseNode * key = p->pn_left;      JSParseNode * key = p->pn_left;
1709      if (key->pn_type != TOK_STRING || ! ATOM_IS_STRING(key->pn_atom)) {      if (key->pn_type != TOK_STRING || ! ATOM_IS_STRING(key->pn_atom)) {
1710        return -1;        result = -1;
1711          goto done;
1712      }      }
1713      char * id_bytes = JS_GetStringBytes(ATOM_TO_STRING(key->pn_atom));      char * id_bytes = JS_GetStringBytes(ATOM_TO_STRING(key->pn_atom));
1714    
1715      /* the value must be an object literal OR an array */      /* the value must be an object literal OR an array */
1716      JSParseNode * value = p->pn_right;      JSParseNode * value = p->pn_right;
1717      if (! (value->pn_type == TOK_RC || value->pn_type == TOK_RB)) {      if (! (value->pn_type == TOK_RC || value->pn_type == TOK_RB)) {
1718        return -1;        result = -1;
1719          goto done;
1720      }      }
1721    
1722      JSParseNode * array = NULL;      JSParseNode * array = NULL;
# Line 1067  Line 1728 
1728      else if (value->pn_type == TOK_RC) {      else if (value->pn_type == TOK_RC) {
1729        /* an object literal */        /* an object literal */
1730        if (value->pn_count != 2) {        if (value->pn_count != 2) {
1731          return -1;          result = -1;
1732            goto done;
1733        }        }
1734        for (JSParseNode * element = value->pn_head; element != NULL; element = element->pn_next) {        for (JSParseNode * element = value->pn_head; element != NULL; element = element->pn_next) {
1735          if (element->pn_type != TOK_COLON) {          if (element->pn_type != TOK_COLON) {
1736            return -1;            result = -1;
1737              goto done;
1738          }          }
1739          JSParseNode * left = element->pn_left;          JSParseNode * left = element->pn_left;
1740          if (left->pn_type != TOK_STRING || ! ATOM_IS_STRING(left->pn_atom)) {          if (left->pn_type != TOK_STRING || ! ATOM_IS_STRING(left->pn_atom)) {
1741            return -1;            result = -1;
1742              goto done;
1743          }          }
1744          const char * s = JS_GetStringBytes(ATOM_TO_STRING(left->pn_atom));          const char * s = JS_GetStringBytes(ATOM_TO_STRING(left->pn_atom));
1745          if (strcmp(s, "coverage") == 0) {          if (strcmp(s, "coverage") == 0) {
1746            array = element->pn_right;            array = element->pn_right;
1747            if (array->pn_type != TOK_RB) {            if (array->pn_type != TOK_RB) {
1748              return -1;              result = -1;
1749                goto done;
1750            }            }
1751          }          }
1752          else if (strcmp(s, "source") == 0) {          else if (strcmp(s, "source") == 0) {
1753            source = element->pn_right;            source = element->pn_right;
1754            if (source->pn_type != TOK_STRING || ! ATOM_IS_STRING(source->pn_atom)) {            if (source->pn_type != TOK_RB) {
1755              return -1;              result = -1;
1756                goto done;
1757            }            }
1758          }          }
1759          else {          else {
1760            return -1;            result = -1;
1761              goto done;
1762          }          }
1763        }        }
1764      }      }
1765      else {      else {
1766        return -1;        result = -1;
1767          goto done;
1768      }      }
1769    
1770      if (array == NULL) {      if (array == NULL) {
1771        return -1;        result = -1;
1772          goto done;
1773      }      }
1774    
1775      /* look up the file in the coverage table */      /* look up the file in the coverage table */
# Line 1110  Line 1779 
1779        char * id = xstrdup(id_bytes);        char * id = xstrdup(id_bytes);
1780        file_coverage = xmalloc(sizeof(FileCoverage));        file_coverage = xmalloc(sizeof(FileCoverage));
1781        file_coverage->id = id;        file_coverage->id = id;
1782        file_coverage->num_lines = array->pn_count - 1;        file_coverage->num_coverage_lines = array->pn_count;
1783        file_coverage->lines = xnew(int, array->pn_count);        file_coverage->coverage_lines = xnew(int, array->pn_count);
1784        if (source == NULL) {        file_coverage->source_lines = NULL;
         file_coverage->source = NULL;  
       }  
       else {  
         file_coverage->source = xstrdup(JS_GetStringBytes(ATOM_TO_STRING(source->pn_atom)));  
       }  
1785    
1786        /* set coverage for all lines */        /* set coverage for all lines */
1787        uint32 i = 0;        uint32 i = 0;
1788        for (JSParseNode * element = array->pn_head; element != NULL; element = element->pn_next, i++) {        for (JSParseNode * element = array->pn_head; element != NULL; element = element->pn_next, i++) {
1789          if (element->pn_type == TOK_NUMBER) {          if (element->pn_type == TOK_NUMBER) {
1790            file_coverage->lines[i] = (int) element->pn_dval;            file_coverage->coverage_lines[i] = (int) element->pn_dval;
1791          }          }
1792          else if (element->pn_type == TOK_PRIMARY && element->pn_op == JSOP_NULL) {          else if (element->pn_type == TOK_PRIMARY && element->pn_op == JSOP_NULL) {
1793            file_coverage->lines[i] = -1;            file_coverage->coverage_lines[i] = -1;
1794          }          }
1795          else {          else {
1796            return -1;            result = -1;
1797              goto done;
1798          }          }
1799        }        }
1800        assert(i == array->pn_count);        assert(i == array->pn_count);
# Line 1144  Line 1809 
1809      else {      else {
1810        /* sanity check */        /* sanity check */
1811        assert(strcmp(file_coverage->id, id_bytes) == 0);        assert(strcmp(file_coverage->id, id_bytes) == 0);
1812        if (file_coverage->num_lines != array->pn_count - 1) {        if (file_coverage->num_coverage_lines != array->pn_count) {
1813          return -2;          result = -2;
1814            goto done;
1815        }        }
1816    
1817        /* merge the coverage */        /* merge the coverage */
1818        uint32 i = 0;        uint32 i = 0;
1819        for (JSParseNode * element = array->pn_head; element != NULL; element = element->pn_next, i++) {        for (JSParseNode * element = array->pn_head; element != NULL; element = element->pn_next, i++) {
1820          if (element->pn_type == TOK_NUMBER) {          if (element->pn_type == TOK_NUMBER) {
1821            if (file_coverage->lines[i] == -1) {            if (file_coverage->coverage_lines[i] == -1) {
1822              return -2;              result = -2;
1823                goto done;
1824            }            }
1825            file_coverage->lines[i] += (int) element->pn_dval;            file_coverage->coverage_lines[i] += (int) element->pn_dval;
1826          }          }
1827          else if (element->pn_type == TOK_PRIMARY && element->pn_op == JSOP_NULL) {          else if (element->pn_type == TOK_PRIMARY && element->pn_op == JSOP_NULL) {
1828            if (file_coverage->lines[i] != -1) {            if (file_coverage->coverage_lines[i] != -1) {
1829              return -2;              result = -2;
1830                goto done;
1831            }            }
1832          }          }
1833          else {          else {
1834            return -1;            result = -1;
1835              goto done;
1836          }          }
1837        }        }
1838        assert(i == array->pn_count);        assert(i == array->pn_count);
1839        }
1840    
1841        /* if this JSON file has source, use it */      /* if this JSON file has source, use it */
1842        if (file_coverage->source == NULL && source != NULL) {      if (file_coverage->source_lines == NULL && source != NULL) {
1843          file_coverage->source = xstrdup(JS_GetStringBytes(ATOM_TO_STRING(source->pn_atom)));        file_coverage->num_source_lines = source->pn_count;
1844          file_coverage->source_lines = xnew(char *, source->pn_count);
1845          uint32 i = 0;
1846          for (JSParseNode * element = source->pn_head; element != NULL; element = element->pn_next, i++) {
1847            if (element->pn_type != TOK_STRING) {
1848              result = -1;
1849              goto done;
1850            }
1851            file_coverage->source_lines[i] = xstrdup(JS_GetStringBytes(ATOM_TO_STRING(element->pn_atom)));
1852        }        }
1853          assert(i == source->pn_count);
1854      }      }
1855    }    }
1856    
1857    return 0;  done:
1858      js_FinishParseContext(context, &parse_context);
1859      return result;
1860  }  }

Legend:
Removed from v.156  
changed lines
  Added in v.392

  ViewVC Help
Powered by ViewVC 1.1.24