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

Legend:
Removed from v.155  
changed lines
  Added in v.377

  ViewVC Help
Powered by ViewVC 1.1.24