/[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 214 by siliconforks, Fri Oct 3 02:26:04 2008 UTC revision 379 by siliconforks, Tue Oct 28 17:50:42 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>
# Line 40  Line 44 
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 52  Line 69 
69  static char * lines = NULL;  static char * lines = NULL;
70  static uint16_t num_lines = 0;  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);
87    if (runtime == NULL) {    if (runtime == NULL) {
# Line 63  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 78  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    size_t length = JSSTRING_LENGTH(s);    size_t length = JSSTRING_LENGTH(s);
139    jschar * characters = JSSTRING_CHARS(s);    jschar * characters = JSSTRING_CHARS(s);
# Line 112  Line 168 
168        case 0xa:        case 0xa:
169          Stream_write_string(f, "\\n");          Stream_write_string(f, "\\n");
170          break;          break;
171          /* IE doesn't support this */
172          /*
173        case 0xb:        case 0xb:
174          Stream_write_string(f, "\\v");          Stream_write_string(f, "\\v");
175          break;          break;
176          */
177        case 0xc:        case 0xc:
178          Stream_write_string(f, "\\f");          Stream_write_string(f, "\\f");
179          break;          break;
# Line 161  Line 220 
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 171  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 204  Line 267 
267    }    }
268  }  }
269    
270  static void instrument_expression(JSParseNode * node, Stream * f);  static void output_expression(JSParseNode * node, Stream * f, bool parenthesize_object_literals);
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      output_expression(node->pn_left, f, false);
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      output_expression(p, f, false);
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        output_expression(if_node->pn_kid1, f, false);
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_type == TOK_FUNCTION);
330    assert(node->pn_arity == PN_FUNC);    assert(node->pn_arity == PN_FUNC);
331    assert(ATOM_IS_OBJECT(node->pn_funAtom));    JSObject * object = node->pn_funpob->object;
   JSObject * object = ATOM_TO_OBJECT(node->pn_funAtom);  
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) {
     Stream_write_char(f, ' ');  
343      print_string_atom(function->atom, f);      print_string_atom(function->atom, f);
344    }    }
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");
358      }      }
     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);  
359    }    }
360      bool destructuring = false;
361    for (int i = 0; i < function->nargs; i++) {    for (int i = 0; i < function->nargs; i++) {
     assert(params[i] != NULL);  
362      if (i > 0) {      if (i > 0) {
363        Stream_write_string(f, ", ");        Stream_write_string(f, ", ");
364      }      }
365      if (ATOM_IS_STRING(params[i])) {      JSAtom * param = JS_LOCAL_NAME_TO_ATOM(local_names[i]);
366        print_string_atom(params[i], f);      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          output_expression(expression, f, false);
385        }
386        else {
387          print_string_atom(param, f);
388      }      }
389    }    }
390      JS_FinishArenaPool(&pool);
391    Stream_write_string(f, ") {\n");    Stream_write_string(f, ") {\n");
   free(params);  
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          output_expression(function_node, f, false);
439          Stream_write_char(f, ')');
440        }
441      }
442      else {
443        output_expression(function_node, f, false);
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      }      }
450      instrument_expression(p, f);      output_expression(p, f, false);
451    }    }
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        if (p != list->pn_head) {
459          Stream_write_string(f, ", ");
460        }
461        output_expression(p, f, false);
462      }
463    }
464    
465  /*  /*
466  See <Expressions> in jsparse.h.  See <Expressions> in jsparse.h.
467  TOK_FUNCTION is handled as a statement and as an expression.  TOK_FUNCTION is handled as a statement and as an expression.
# Line 291  Line 475 
475  TOK_INSTANCEOF  binary  TOK_INSTANCEOF  binary
476  TOK_IN          binary  TOK_IN          binary
477  */  */
478  static void instrument_expression(JSParseNode * node, Stream * f) {  static void output_expression(JSParseNode * node, Stream * f, bool parenthesize_object_literals) {
479    switch (node->pn_type) {    switch (node->pn_type) {
480    case TOK_FUNCTION:    case TOK_FUNCTION:
481      instrument_function(node, f, 0, FUNCTION_NORMAL);      instrument_function(node, f, 0, FUNCTION_NORMAL);
# Line 301  Line 485 
485        if (p != node->pn_head) {        if (p != node->pn_head) {
486          Stream_write_string(f, ", ");          Stream_write_string(f, ", ");
487        }        }
488        instrument_expression(p, f);        output_expression(p, f, parenthesize_object_literals);
489      }      }
490      break;      break;
491    case TOK_ASSIGN:    case TOK_ASSIGN:
492      instrument_expression(node->pn_left, f);      output_expression(node->pn_left, f, parenthesize_object_literals);
493      Stream_write_char(f, ' ');      Stream_write_char(f, ' ');
494      switch (node->pn_op) {      switch (node->pn_op) {
495      case JSOP_ADD:      case JSOP_ADD:
# Line 326  Line 510 
510        break;        break;
511      }      }
512      Stream_write_string(f, "= ");      Stream_write_string(f, "= ");
513      instrument_expression(node->pn_right, f);      output_expression(node->pn_right, f, false);
514      break;      break;
515    case TOK_HOOK:    case TOK_HOOK:
516      instrument_expression(node->pn_kid1, f);      output_expression(node->pn_kid1, f, parenthesize_object_literals);
517      Stream_write_string(f, "? ");      Stream_write_string(f, "? ");
518      instrument_expression(node->pn_kid2, f);      output_expression(node->pn_kid2, f, false);
519      Stream_write_string(f, ": ");      Stream_write_string(f, ": ");
520      instrument_expression(node->pn_kid3, f);      output_expression(node->pn_kid3, f, false);
521      break;      break;
522    case TOK_OR:    case TOK_OR:
     instrument_expression(node->pn_left, f);  
     Stream_write_string(f, " || ");  
     instrument_expression(node->pn_right, f);  
     break;  
523    case TOK_AND:    case TOK_AND:
     instrument_expression(node->pn_left, f);  
     Stream_write_string(f, " && ");  
     instrument_expression(node->pn_right, f);  
     break;  
524    case TOK_BITOR:    case TOK_BITOR:
525    case TOK_BITXOR:    case TOK_BITXOR:
526    case TOK_BITAND:    case TOK_BITAND:
# Line 357  Line 533 
533    case TOK_DIVOP:    case TOK_DIVOP:
534      switch (node->pn_arity) {      switch (node->pn_arity) {
535      case PN_BINARY:      case PN_BINARY:
536        instrument_expression(node->pn_left, f);        output_expression(node->pn_left, f, parenthesize_object_literals);
537        Stream_printf(f, " %s ", get_op(node->pn_op));        Stream_printf(f, " %s ", get_op(node->pn_op));
538        instrument_expression(node->pn_right, f);        output_expression(node->pn_right, f, false);
539        break;        break;
540      case PN_LIST:      case PN_LIST:
541        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) {
542          if (p != node->pn_head) {          if (p == node->pn_head) {
543              output_expression(p, f, parenthesize_object_literals);
544            }
545            else {
546            Stream_printf(f, " %s ", get_op(node->pn_op));            Stream_printf(f, " %s ", get_op(node->pn_op));
547              output_expression(p, f, false);
548          }          }
         instrument_expression(p, f);  
549        }        }
550        break;        break;
551      default:      default:
# Line 377  Line 556 
556      switch (node->pn_op) {      switch (node->pn_op) {
557      case JSOP_NEG:      case JSOP_NEG:
558        Stream_write_char(f, '-');        Stream_write_char(f, '-');
559        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, false);
560        break;        break;
561      case JSOP_POS:      case JSOP_POS:
562        Stream_write_char(f, '+');        Stream_write_char(f, '+');
563        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, false);
564        break;        break;
565      case JSOP_NOT:      case JSOP_NOT:
566        Stream_write_char(f, '!');        Stream_write_char(f, '!');
567        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, false);
568        break;        break;
569      case JSOP_BITNOT:      case JSOP_BITNOT:
570        Stream_write_char(f, '~');        Stream_write_char(f, '~');
571        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, false);
572        break;        break;
573      case JSOP_TYPEOF:      case JSOP_TYPEOF:
574        Stream_write_string(f, "typeof ");        Stream_write_string(f, "typeof ");
575        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, false);
576        break;        break;
577      case JSOP_VOID:      case JSOP_VOID:
578        Stream_write_string(f, "void ");        Stream_write_string(f, "void ");
579        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, false);
580        break;        break;
581      default:      default:
582        abort();        fatal_source(file_id, node->pn_pos.begin.lineno, "unknown operator (%d)", node->pn_op);
583        break;        break;
584      }      }
585      break;      break;
# Line 414  Line 593 
593      case JSOP_INCPROP:      case JSOP_INCPROP:
594      case JSOP_INCELEM:      case JSOP_INCELEM:
595        Stream_write_string(f, "++");        Stream_write_string(f, "++");
596        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, false);
597        break;        break;
598      case JSOP_DECNAME:      case JSOP_DECNAME:
599      case JSOP_DECPROP:      case JSOP_DECPROP:
600      case JSOP_DECELEM:      case JSOP_DECELEM:
601        Stream_write_string(f, "--");        Stream_write_string(f, "--");
602        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, false);
603        break;        break;
604      case JSOP_NAMEINC:      case JSOP_NAMEINC:
605      case JSOP_PROPINC:      case JSOP_PROPINC:
606      case JSOP_ELEMINC:      case JSOP_ELEMINC:
607        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, parenthesize_object_literals);
608        Stream_write_string(f, "++");        Stream_write_string(f, "++");
609        break;        break;
610      case JSOP_NAMEDEC:      case JSOP_NAMEDEC:
611      case JSOP_PROPDEC:      case JSOP_PROPDEC:
612      case JSOP_ELEMDEC:      case JSOP_ELEMDEC:
613        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, parenthesize_object_literals);
614        Stream_write_string(f, "--");        Stream_write_string(f, "--");
615        break;        break;
616      default:      default:
# Line 445  Line 624 
624      break;      break;
625    case TOK_DELETE:    case TOK_DELETE:
626      Stream_write_string(f, "delete ");      Stream_write_string(f, "delete ");
627      instrument_expression(node->pn_kid, f);      output_expression(node->pn_kid, f, false);
628      break;      break;
629    case TOK_DOT:    case TOK_DOT:
630        /* numeric literals must be parenthesized */
631        switch (node->pn_expr->pn_type) {
632        case TOK_NUMBER:
633          Stream_write_char(f, '(');
634          output_expression(node->pn_expr, f, false);
635          Stream_write_char(f, ')');
636          break;
637        default:
638          output_expression(node->pn_expr, f, true);
639          break;
640        }
641      /*      /*
642      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'
643      contains illegal characters, we have to use the subscript syntax instead of      contains illegal characters, we have to use the subscript syntax instead of
644      the dot syntax.      the dot syntax.
645      */      */
     instrument_expression(node->pn_expr, f);  
646      assert(ATOM_IS_STRING(node->pn_atom));      assert(ATOM_IS_STRING(node->pn_atom));
647      {      {
648        JSString * s = ATOM_TO_STRING(node->pn_atom);        JSString * s = ATOM_TO_STRING(node->pn_atom);
649        /* XXX - semantics changed in 1.7 */        bool must_quote;
650        if (! ATOM_KEYWORD(node->pn_atom) && js_IsIdentifier(s)) {        if (JSSTRING_LENGTH(s) == 0) {
651          Stream_write_char(f, '.');          must_quote = true;
652          print_string_atom(node->pn_atom, f);        }
653          else if (js_CheckKeyword(JSSTRING_CHARS(s), JSSTRING_LENGTH(s)) != TOK_EOF) {
654            must_quote = true;
655          }
656          else if (! js_IsIdentifier(s)) {
657            must_quote = true;
658        }        }
659        else {        else {
660            must_quote = false;
661          }
662          if (must_quote) {
663          Stream_write_char(f, '[');          Stream_write_char(f, '[');
664          print_quoted_string_atom(node->pn_atom, f);          print_quoted_string_atom(node->pn_atom, f);
665          Stream_write_char(f, ']');          Stream_write_char(f, ']');
666        }        }
667          else {
668            Stream_write_char(f, '.');
669            print_string_atom(node->pn_atom, f);
670          }
671      }      }
672      break;      break;
673    case TOK_LB:    case TOK_LB:
674      instrument_expression(node->pn_left, f);      output_expression(node->pn_left, f, false);
675      Stream_write_char(f, '[');      Stream_write_char(f, '[');
676      instrument_expression(node->pn_right, f);      output_expression(node->pn_right, f, false);
677      Stream_write_char(f, ']');      Stream_write_char(f, ']');
678      break;      break;
679    case TOK_LP:    case TOK_LP:
# Line 486  Line 687 
687        }        }
688        /* TOK_COMMA is a special case: a hole in the array */        /* TOK_COMMA is a special case: a hole in the array */
689        if (p->pn_type != TOK_COMMA) {        if (p->pn_type != TOK_COMMA) {
690          instrument_expression(p, f);          output_expression(p, f, false);
691        }        }
692      }      }
693      if (node->pn_extra == PNX_ENDCOMMA) {      if (node->pn_extra == PNX_ENDCOMMA) {
# Line 495  Line 696 
696      Stream_write_char(f, ']');      Stream_write_char(f, ']');
697      break;      break;
698    case TOK_RC:    case TOK_RC:
699        if (parenthesize_object_literals) {
700          Stream_write_char(f, '(');
701        }
702      Stream_write_char(f, '{');      Stream_write_char(f, '{');
703      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) {
704        assert(p->pn_type == TOK_COLON);        if (p->pn_type != TOK_COLON) {
705            fatal_source(file_id, p->pn_pos.begin.lineno, "unsupported node type (%d)", p->pn_type);
706          }
707        if (p != node->pn_head) {        if (p != node->pn_head) {
708          Stream_write_string(f, ", ");          Stream_write_string(f, ", ");
709        }        }
# Line 505  Line 711 
711        /* check whether this is a getter or setter */        /* check whether this is a getter or setter */
712        switch (p->pn_op) {        switch (p->pn_op) {
713        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;  
714        case JSOP_SETTER:        case JSOP_SETTER:
715          Stream_write_string(f, "set ");          if (p->pn_op == JSOP_GETTER) {
716          instrument_expression(p->pn_left, f);            Stream_write_string(f, "get ");
717            }
718            else {
719              Stream_write_string(f, "set ");
720            }
721            output_expression(p->pn_left, f, false);
722            if (p->pn_right->pn_type != TOK_FUNCTION) {
723              fatal_source(file_id, p->pn_pos.begin.lineno, "expected function");
724            }
725          instrument_function(p->pn_right, f, 0, FUNCTION_GETTER_OR_SETTER);          instrument_function(p->pn_right, f, 0, FUNCTION_GETTER_OR_SETTER);
726          break;          break;
727        default:        default:
728          instrument_expression(p->pn_left, f);          output_expression(p->pn_left, f, false);
729          Stream_write_string(f, ": ");          Stream_write_string(f, ": ");
730          instrument_expression(p->pn_right, f);          output_expression(p->pn_right, f, false);
731          break;          break;
732        }        }
733      }      }
734      Stream_write_char(f, '}');      Stream_write_char(f, '}');
735        if (parenthesize_object_literals) {
736          Stream_write_char(f, ')');
737        }
738      break;      break;
739    case TOK_RP:    case TOK_RP:
740      Stream_write_char(f, '(');      Stream_write_char(f, '(');
741      instrument_expression(node->pn_kid, f);      output_expression(node->pn_kid, f, false);
742      Stream_write_char(f, ')');      Stream_write_char(f, ')');
743      break;      break;
744    case TOK_NAME:    case TOK_NAME:
745      print_string_atom(node->pn_atom, f);      print_string_atom(node->pn_atom, f);
746        if (node->pn_expr != NULL) {
747          Stream_write_string(f, " = ");
748          output_expression(node->pn_expr, f, false);
749        }
750      break;      break;
751    case TOK_STRING:    case TOK_STRING:
752      print_quoted_string_atom(node->pn_atom, f);      print_quoted_string_atom(node->pn_atom, f);
753      break;      break;
754    case TOK_OBJECT:    case TOK_REGEXP:
755      switch (node->pn_op) {      assert(node->pn_op == JSOP_REGEXP);
756      case JSOP_OBJECT:      {
757        /* I assume this is JSOP_REGEXP */        JSObject * object = node->pn_pob->object;
758        abort();        jsval result;
759        break;        js_regexp_toString(context, object, &result);
760      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_regex(result, f);  
       }  
       break;  
     default:  
       abort();  
       break;  
761      }      }
762      break;      break;
763    case TOK_NUMBER:    case TOK_NUMBER:
# Line 588  Line 794 
794      }      }
795      break;      break;
796    case TOK_INSTANCEOF:    case TOK_INSTANCEOF:
797      instrument_expression(node->pn_left, f);      output_expression(node->pn_left, f, parenthesize_object_literals);
798      Stream_write_string(f, " instanceof ");      Stream_write_string(f, " instanceof ");
799      instrument_expression(node->pn_right, f);      output_expression(node->pn_right, f, false);
800      break;      break;
801    case TOK_IN:    case TOK_IN:
802      instrument_expression(node->pn_left, f);      output_expression(node->pn_left, f, false);
803      Stream_write_string(f, " in ");      Stream_write_string(f, " in ");
804      instrument_expression(node->pn_right, f);      output_expression(node->pn_right, f, false);
805      break;      break;
806    default:    case TOK_LEXICALSCOPE:
807      fatal("unsupported node type in file %s: %d", file_id, node->pn_type);      assert(node->pn_arity == PN_NAME);
808    }      assert(node->pn_expr->pn_type == TOK_LET);
809  }      assert(node->pn_expr->pn_arity == PN_BINARY);
810        Stream_write_string(f, "let(");
811  static void instrument_var_statement(JSParseNode * node, Stream * f, int indent) {      assert(node->pn_expr->pn_left->pn_type == TOK_LP);
812    assert(node->pn_arity == PN_LIST);      assert(node->pn_expr->pn_left->pn_arity == PN_LIST);
813    Stream_printf(f, "%*s", indent, "");      instrument_declarations(node->pn_expr->pn_left, f);
814    Stream_write_string(f, "var ");      Stream_write_string(f, ") ");
815    for (struct JSParseNode * p = node->pn_u.list.head; p != NULL; p = p->pn_next) {      output_expression(node->pn_expr->pn_right, f, true);
816      assert(p->pn_type == TOK_NAME);      break;
817      assert(p->pn_arity == PN_NAME);    case TOK_YIELD:
818      if (p != node->pn_head) {      assert(node->pn_arity == PN_UNARY);
819        Stream_write_string(f, ", ");      Stream_write_string(f, "yield");
820        if (node->pn_kid != NULL) {
821          Stream_write_char(f, ' ');
822          output_expression(node->pn_kid, f, true);
823      }      }
824      print_string_atom(p->pn_atom, f);      break;
825      if (p->pn_expr != NULL) {    case TOK_ARRAYCOMP:
826        Stream_write_string(f, " = ");      assert(node->pn_arity == PN_LIST);
827        instrument_expression(p->pn_expr, f);      {
828          JSParseNode * block_node;
829          switch (node->pn_count) {
830          case 1:
831            block_node = node->pn_head;
832            break;
833          case 2:
834            block_node = node->pn_head->pn_next;
835            break;
836          default:
837            abort();
838            break;
839          }
840          Stream_write_char(f, '[');
841          output_array_comprehension_or_generator_expression(block_node, f);
842          Stream_write_char(f, ']');
843      }      }
844        break;
845      case TOK_VAR:
846        assert(node->pn_arity == PN_LIST);
847        Stream_write_string(f, "var ");
848        instrument_declarations(node, f);
849        break;
850      case TOK_LET:
851        assert(node->pn_arity == PN_LIST);
852        Stream_write_string(f, "let ");
853        instrument_declarations(node, f);
854        break;
855      default:
856        fatal_source(file_id, node->pn_pos.begin.lineno, "unsupported node type (%d)", node->pn_type);
857    }    }
858  }  }
859    
860  static void output_statement(JSParseNode * node, Stream * f, int indent) {  static void output_statement(JSParseNode * node, Stream * f, int indent, bool is_jscoverage_if) {
861    switch (node->pn_type) {    switch (node->pn_type) {
862    case TOK_FUNCTION:    case TOK_FUNCTION:
863      instrument_function(node, f, indent, FUNCTION_NORMAL);      instrument_function(node, f, indent, FUNCTION_NORMAL);
# Line 631  Line 868 
868      Stream_write_string(f, "{\n");      Stream_write_string(f, "{\n");
869  */  */
870      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) {
871        instrument_statement(p, f, indent);        instrument_statement(p, f, indent, false);
872      }      }
873  /*  /*
874      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
# Line 639  Line 876 
876  */  */
877      break;      break;
878    case TOK_IF:    case TOK_IF:
879      {
880      assert(node->pn_arity == PN_TERNARY);      assert(node->pn_arity == PN_TERNARY);
881    
882        uint16_t line = node->pn_pos.begin.lineno;
883        if (! is_jscoverage_if) {
884          if (line > num_lines) {
885            fatal("file %s contains more than 65,535 lines", file_id);
886          }
887          if (line >= 2 && exclusive_directives[line - 2]) {
888            is_jscoverage_if = true;
889          }
890        }
891    
892      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
893      Stream_write_string(f, "if (");      Stream_write_string(f, "if (");
894      instrument_expression(node->pn_kid1, f);      output_expression(node->pn_kid1, f, false);
895      Stream_write_string(f, ") {\n");      Stream_write_string(f, ") {\n");
896      instrument_statement(node->pn_kid2, f, indent + 2);      if (is_jscoverage_if && node->pn_kid3) {
897          uint16_t else_start = node->pn_kid3->pn_pos.begin.lineno;
898          uint16_t else_end = node->pn_kid3->pn_pos.end.lineno + 1;
899          Stream_printf(f, "%*s", indent + 2, "");
900          Stream_printf(f, "_$jscoverage['%s'].conditionals[%d] = %d;\n", file_id, else_start, else_end);
901        }
902        instrument_statement(node->pn_kid2, f, indent + 2, false);
903      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
904      Stream_write_string(f, "}\n");      Stream_write_string(f, "}\n");
905      if (node->pn_kid3) {  
906        if (node->pn_kid3 || is_jscoverage_if) {
907        Stream_printf(f, "%*s", indent, "");        Stream_printf(f, "%*s", indent, "");
908        Stream_write_string(f, "else {\n");        Stream_write_string(f, "else {\n");
909        instrument_statement(node->pn_kid3, f, indent + 2);  
910          if (is_jscoverage_if) {
911            uint16_t if_start = node->pn_kid2->pn_pos.begin.lineno + 1;
912            uint16_t if_end = node->pn_kid2->pn_pos.end.lineno + 1;
913            Stream_printf(f, "%*s", indent + 2, "");
914            Stream_printf(f, "_$jscoverage['%s'].conditionals[%d] = %d;\n", file_id, if_start, if_end);
915          }
916    
917          if (node->pn_kid3) {
918            instrument_statement(node->pn_kid3, f, indent + 2, is_jscoverage_if);
919          }
920    
921        Stream_printf(f, "%*s", indent, "");        Stream_printf(f, "%*s", indent, "");
922        Stream_write_string(f, "}\n");        Stream_write_string(f, "}\n");
923      }      }
924    
925      break;      break;
926      }
927    case TOK_SWITCH:    case TOK_SWITCH:
928      assert(node->pn_arity == PN_BINARY);      assert(node->pn_arity == PN_BINARY);
929      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
930      Stream_write_string(f, "switch (");      Stream_write_string(f, "switch (");
931      instrument_expression(node->pn_left, f);      output_expression(node->pn_left, f, false);
932      Stream_write_string(f, ") {\n");      Stream_write_string(f, ") {\n");
933      for (struct JSParseNode * p = node->pn_right->pn_head; p != NULL; p = p->pn_next) {      {
934        Stream_printf(f, "%*s", indent, "");        JSParseNode * list = node->pn_right;
935        switch (p->pn_type) {        if (list->pn_type == TOK_LEXICALSCOPE) {
936        case TOK_CASE:          list = list->pn_expr;
937          Stream_write_string(f, "case ");        }
938          instrument_expression(p->pn_left, f);        for (struct JSParseNode * p = list->pn_head; p != NULL; p = p->pn_next) {
939          Stream_write_string(f, ":\n");          Stream_printf(f, "%*s", indent, "");
940          break;          switch (p->pn_type) {
941        case TOK_DEFAULT:          case TOK_CASE:
942          Stream_write_string(f, "default:\n");            Stream_write_string(f, "case ");
943          break;            output_expression(p->pn_left, f, false);
944        default:            Stream_write_string(f, ":\n");
945          abort();            break;
946          break;          case TOK_DEFAULT:
947              Stream_write_string(f, "default:\n");
948              break;
949            default:
950              abort();
951              break;
952            }
953            instrument_statement(p->pn_right, f, indent + 2, false);
954        }        }
       instrument_statement(p->pn_right, f, indent + 2);  
955      }      }
956      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
957      Stream_write_string(f, "}\n");      Stream_write_string(f, "}\n");
# Line 689  Line 964 
964      assert(node->pn_arity == PN_BINARY);      assert(node->pn_arity == PN_BINARY);
965      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
966      Stream_write_string(f, "while (");      Stream_write_string(f, "while (");
967      instrument_expression(node->pn_left, f);      output_expression(node->pn_left, f, false);
968      Stream_write_string(f, ") {\n");      Stream_write_string(f, ") {\n");
969      instrument_statement(node->pn_right, f, indent + 2);      instrument_statement(node->pn_right, f, indent + 2, false);
970      Stream_write_string(f, "}\n");      Stream_write_string(f, "}\n");
971      break;      break;
972    case TOK_DO:    case TOK_DO:
973      assert(node->pn_arity == PN_BINARY);      assert(node->pn_arity == PN_BINARY);
974      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
975      Stream_write_string(f, "do {\n");      Stream_write_string(f, "do {\n");
976      instrument_statement(node->pn_left, f, indent + 2);      instrument_statement(node->pn_left, f, indent + 2, false);
977      Stream_write_string(f, "}\n");      Stream_write_string(f, "}\n");
978      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
979      Stream_write_string(f, "while (");      Stream_write_string(f, "while (");
980      instrument_expression(node->pn_right, f);      output_expression(node->pn_right, f, false);
981      Stream_write_string(f, ");\n");      Stream_write_string(f, ");\n");
982      break;      break;
983    case TOK_FOR:    case TOK_FOR:
984      assert(node->pn_arity == PN_BINARY);      assert(node->pn_arity == PN_BINARY);
985      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
     Stream_write_string(f, "for (");  
986      switch (node->pn_left->pn_type) {      switch (node->pn_left->pn_type) {
987      case TOK_IN:      case TOK_IN:
988        /* for/in */        /* for/in */
989        assert(node->pn_left->pn_arity == PN_BINARY);        assert(node->pn_left->pn_arity == PN_BINARY);
990        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);  
991        break;        break;
992      case TOK_RESERVED:      case TOK_RESERVED:
993        /* for (;;) */        /* for (;;) */
994        assert(node->pn_left->pn_arity == PN_TERNARY);        assert(node->pn_left->pn_arity == PN_TERNARY);
995          Stream_write_string(f, "for (");
996        if (node->pn_left->pn_kid1) {        if (node->pn_left->pn_kid1) {
997          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);  
         }  
998        }        }
999        Stream_write_string(f, ";");        Stream_write_string(f, ";");
1000        if (node->pn_left->pn_kid2) {        if (node->pn_left->pn_kid2) {
1001          Stream_write_char(f, ' ');          Stream_write_char(f, ' ');
1002          instrument_expression(node->pn_left->pn_kid2, f);          output_expression(node->pn_left->pn_kid2, f, false);
1003        }        }
1004        Stream_write_string(f, ";");        Stream_write_string(f, ";");
1005        if (node->pn_left->pn_kid3) {        if (node->pn_left->pn_kid3) {
1006          Stream_write_char(f, ' ');          Stream_write_char(f, ' ');
1007          instrument_expression(node->pn_left->pn_kid3, f);          output_expression(node->pn_left->pn_kid3, f, false);
1008        }        }
1009          Stream_write_char(f, ')');
1010        break;        break;
1011      default:      default:
1012        abort();        abort();
1013        break;        break;
1014      }      }
1015      Stream_write_string(f, ") {\n");      Stream_write_string(f, " {\n");
1016      instrument_statement(node->pn_right, f, indent + 2);      instrument_statement(node->pn_right, f, indent + 2, false);
1017      Stream_write_string(f, "}\n");      Stream_write_string(f, "}\n");
1018      break;      break;
1019    case TOK_THROW:    case TOK_THROW:
1020      assert(node->pn_arity == PN_UNARY);      assert(node->pn_arity == PN_UNARY);
1021      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
1022      Stream_write_string(f, "throw ");      Stream_write_string(f, "throw ");
1023      instrument_expression(node->pn_u.unary.kid, f);      output_expression(node->pn_u.unary.kid, f, false);
1024      Stream_write_string(f, ";\n");      Stream_write_string(f, ";\n");
1025      break;      break;
1026    case TOK_TRY:    case TOK_TRY:
1027      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
1028      Stream_write_string(f, "try {\n");      Stream_write_string(f, "try {\n");
1029      instrument_statement(node->pn_kid1, f, indent + 2);      instrument_statement(node->pn_kid1, f, indent + 2, false);
1030      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
1031      Stream_write_string(f, "}\n");      Stream_write_string(f, "}\n");
1032      {      if (node->pn_kid2) {
1033        for (JSParseNode * catch = node->pn_kid2; catch != NULL; catch = catch->pn_kid2) {        assert(node->pn_kid2->pn_type == TOK_RESERVED);
1034          for (JSParseNode * scope = node->pn_kid2->pn_head; scope != NULL; scope = scope->pn_next) {
1035            assert(scope->pn_type == TOK_LEXICALSCOPE);
1036            JSParseNode * catch = scope->pn_expr;
1037          assert(catch->pn_type == TOK_CATCH);          assert(catch->pn_type == TOK_CATCH);
1038          Stream_printf(f, "%*s", indent, "");          Stream_printf(f, "%*s", indent, "");
1039          Stream_write_string(f, "catch (");          Stream_write_string(f, "catch (");
1040          assert(catch->pn_kid1->pn_arity == PN_NAME);          output_expression(catch->pn_kid1, f, false);
1041          print_string_atom(catch->pn_kid1->pn_atom, f);          if (catch->pn_kid2) {
         if (catch->pn_kid1->pn_expr) {  
1042            Stream_write_string(f, " if ");            Stream_write_string(f, " if ");
1043            instrument_expression(catch->pn_kid1->pn_expr, f);            output_expression(catch->pn_kid2, f, false);
1044          }          }
1045          Stream_write_string(f, ") {\n");          Stream_write_string(f, ") {\n");
1046          instrument_statement(catch->pn_kid3, f, indent + 2);          instrument_statement(catch->pn_kid3, f, indent + 2, false);
1047          Stream_printf(f, "%*s", indent, "");          Stream_printf(f, "%*s", indent, "");
1048          Stream_write_string(f, "}\n");          Stream_write_string(f, "}\n");
1049        }        }
# Line 797  Line 1051 
1051      if (node->pn_kid3) {      if (node->pn_kid3) {
1052        Stream_printf(f, "%*s", indent, "");        Stream_printf(f, "%*s", indent, "");
1053        Stream_write_string(f, "finally {\n");        Stream_write_string(f, "finally {\n");
1054        instrument_statement(node->pn_kid3, f, indent + 2);        instrument_statement(node->pn_kid3, f, indent + 2, false);
1055        Stream_printf(f, "%*s", indent, "");        Stream_printf(f, "%*s", indent, "");
1056        Stream_write_string(f, "}\n");        Stream_write_string(f, "}\n");
1057      }      }
# Line 821  Line 1075 
1075      assert(node->pn_arity == PN_BINARY);      assert(node->pn_arity == PN_BINARY);
1076      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
1077      Stream_write_string(f, "with (");      Stream_write_string(f, "with (");
1078      instrument_expression(node->pn_left, f);      output_expression(node->pn_left, f, false);
1079      Stream_write_string(f, ") {\n");      Stream_write_string(f, ") {\n");
1080      instrument_statement(node->pn_right, f, indent + 2);      instrument_statement(node->pn_right, f, indent + 2, false);
1081      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
1082      Stream_write_string(f, "}\n");      Stream_write_string(f, "}\n");
1083      break;      break;
1084    case TOK_VAR:    case TOK_VAR:
1085      instrument_var_statement(node, f, indent);      Stream_printf(f, "%*s", indent, "");
1086        output_expression(node, f, false);
1087      Stream_write_string(f, ";\n");      Stream_write_string(f, ";\n");
1088      break;      break;
1089    case TOK_RETURN:    case TOK_RETURN:
# Line 837  Line 1092 
1092      Stream_write_string(f, "return");      Stream_write_string(f, "return");
1093      if (node->pn_kid != NULL) {      if (node->pn_kid != NULL) {
1094        Stream_write_char(f, ' ');        Stream_write_char(f, ' ');
1095        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, true);
1096      }      }
1097      Stream_write_string(f, ";\n");      Stream_write_string(f, ";\n");
1098      break;      break;
# Line 845  Line 1100 
1100      assert(node->pn_arity == PN_UNARY);      assert(node->pn_arity == PN_UNARY);
1101      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
1102      if (node->pn_kid != NULL) {      if (node->pn_kid != NULL) {
1103        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, true);
1104      }      }
1105      Stream_write_string(f, ";\n");      Stream_write_string(f, ";\n");
1106      break;      break;
1107    case TOK_COLON:    case TOK_COLON:
1108      {
1109      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 ...  
     */  
1110      Stream_printf(f, "%*s", indent < 2? 0: indent - 2, "");      Stream_printf(f, "%*s", indent < 2? 0: indent - 2, "");
1111      print_string_atom(node->pn_atom, f);      print_string_atom(node->pn_atom, f);
1112      Stream_write_string(f, ":\n");      Stream_write_string(f, ":\n");
1113      /*      JSParseNode * labelled = node->pn_expr;
1114      ... use output_statement instead of instrument_statement.      if (labelled->pn_type == TOK_LEXICALSCOPE) {
1115      */        labelled = labelled->pn_expr;
1116      output_statement(node->pn_expr, f, indent);      }
1117        if (labelled->pn_type == TOK_LC) {
1118          /* labelled block */
1119          Stream_printf(f, "%*s", indent, "");
1120          Stream_write_string(f, "{\n");
1121          instrument_statement(labelled, f, indent + 2, false);
1122          Stream_printf(f, "%*s", indent, "");
1123          Stream_write_string(f, "}\n");
1124        }
1125        else {
1126          /*
1127          This one is tricky: can't output instrumentation between the label and the
1128          statement it's supposed to label, so use output_statement instead of
1129          instrument_statement.
1130          */
1131          output_statement(labelled, f, indent, false);
1132        }
1133        break;
1134      }
1135      case TOK_LEXICALSCOPE:
1136        /* let statement */
1137        assert(node->pn_arity == PN_NAME);
1138        switch (node->pn_expr->pn_type) {
1139        case TOK_LET:
1140          /* let statement */
1141          assert(node->pn_expr->pn_arity == PN_BINARY);
1142          instrument_statement(node->pn_expr, f, indent, false);
1143          break;
1144        case TOK_LC:
1145          /* block */
1146          Stream_printf(f, "%*s", indent, "");
1147          Stream_write_string(f, "{\n");
1148          instrument_statement(node->pn_expr, f, indent + 2, false);
1149          Stream_printf(f, "%*s", indent, "");
1150          Stream_write_string(f, "}\n");
1151          break;
1152        case TOK_FOR:
1153          instrument_statement(node->pn_expr, f, indent, false);
1154          break;
1155        default:
1156          abort();
1157          break;
1158        }
1159        break;
1160      case TOK_LET:
1161        switch (node->pn_arity) {
1162        case PN_BINARY:
1163          /* let statement */
1164          Stream_printf(f, "%*s", indent, "");
1165          Stream_write_string(f, "let (");
1166          assert(node->pn_left->pn_type == TOK_LP);
1167          assert(node->pn_left->pn_arity == PN_LIST);
1168          instrument_declarations(node->pn_left, f);
1169          Stream_write_string(f, ") {\n");
1170          instrument_statement(node->pn_right, f, indent + 2, false);
1171          Stream_printf(f, "%*s", indent, "");
1172          Stream_write_string(f, "}\n");
1173          break;
1174        case PN_LIST:
1175          /* let definition */
1176          Stream_printf(f, "%*s", indent, "");
1177          output_expression(node, f, false);
1178          Stream_write_string(f, ";\n");
1179          break;
1180        default:
1181          abort();
1182          break;
1183        }
1184        break;
1185      case TOK_DEBUGGER:
1186        Stream_printf(f, "%*s", indent, "");
1187        Stream_write_string(f, "debugger;\n");
1188      break;      break;
1189    default:    default:
1190      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);
1191    }    }
1192  }  }
1193    
# Line 873  Line 1196 
1196  TOK_FUNCTION is handled as a statement and as an expression.  TOK_FUNCTION is handled as a statement and as an expression.
1197  TOK_EXPORT, TOK_IMPORT are not handled.  TOK_EXPORT, TOK_IMPORT are not handled.
1198  */  */
1199  static void instrument_statement(JSParseNode * node, Stream * f, int indent) {  static void instrument_statement(JSParseNode * node, Stream * f, int indent, bool is_jscoverage_if) {
1200    if (node->pn_type != TOK_LC) {    if (node->pn_type != TOK_LC && node->pn_type != TOK_LEXICALSCOPE) {
1201      uint16_t line = node->pn_pos.begin.lineno;      uint16_t line = node->pn_pos.begin.lineno;
1202      if (line > num_lines) {      if (line > num_lines) {
1203        fatal("%s: script contains more than 65,535 lines", file_id);        fatal("file %s contains more than 65,535 lines", file_id);
1204      }      }
1205    
1206      /* the root node has line number 0 */      /* the root node has line number 0 */
# Line 887  Line 1210 
1210        lines[line - 1] = 1;        lines[line - 1] = 1;
1211      }      }
1212    }    }
1213    output_statement(node, f, indent);    output_statement(node, f, indent, is_jscoverage_if);
1214  }  }
1215    
1216  static bool characters_start_with(const jschar * characters, size_t line_start, size_t line_end, const char * prefix) {  static bool characters_start_with(const jschar * characters, size_t line_start, size_t line_end, const char * prefix) {
# Line 909  Line 1232 
1232    }    }
1233  }  }
1234    
1235    static bool characters_are_white_space(const jschar * characters, size_t line_start, size_t line_end) {
1236      /* XXX - other Unicode space */
1237      const jschar * end = characters + line_end;
1238      for (const jschar * p = characters + line_start; p < end; p++) {
1239        jschar c = *p;
1240        if (c == 0x9 || c == 0xB || c == 0xC || c == 0x20 || c == 0xA0) {
1241          continue;
1242        }
1243        else {
1244          return false;
1245        }
1246      }
1247      return true;
1248    }
1249    
1250    static void error_reporter(JSContext * context, const char * message, JSErrorReport * report) {
1251      warn_source(file_id, report->lineno, "%s", message);
1252    }
1253    
1254  void jscoverage_instrument_js(const char * id, const uint16_t * characters, size_t num_characters, Stream * output) {  void jscoverage_instrument_js(const char * id, const uint16_t * characters, size_t num_characters, Stream * output) {
1255    file_id = id;    file_id = id;
1256    
   /* scan the javascript */  
   JSTokenStream * token_stream = js_NewTokenStream(context, characters, num_characters, NULL, 1, NULL);  
   if (token_stream == NULL) {  
     fatal("cannot create token stream from file: %s", file_id);  
   }  
   
1257    /* parse the javascript */    /* parse the javascript */
1258    JSParseNode * node = js_ParseTokenStream(context, global, token_stream);    JSParseContext parse_context;
1259      if (! js_InitParseContext(context, &parse_context, NULL, NULL, characters, num_characters, NULL, NULL, 1)) {
1260        fatal("cannot create token stream from file %s", file_id);
1261      }
1262      JSErrorReporter old_error_reporter = JS_SetErrorReporter(context, error_reporter);
1263      JSParseNode * node = js_ParseScript(context, global, &parse_context);
1264    if (node == NULL) {    if (node == NULL) {
1265      fatal("parse error in file: %s", file_id);      js_ReportUncaughtException(context);
1266        fatal("parse error in file %s", file_id);
1267    }    }
1268      JS_SetErrorReporter(context, old_error_reporter);
1269    num_lines = node->pn_pos.end.lineno;    num_lines = node->pn_pos.end.lineno;
1270    lines = xmalloc(num_lines);    lines = xmalloc(num_lines);
1271    for (int i = 0; i < num_lines; i++) {    for (unsigned int i = 0; i < num_lines; i++) {
1272      lines[i] = 0;      lines[i] = 0;
1273    }    }
1274    
1275    /*    /* search code for conditionals */
1276    An instrumented JavaScript file has 3 sections:    exclusive_directives = xnew(bool, num_lines);
1277    1. initialization    for (unsigned int i = 0; i < num_lines; i++) {
1278    2. instrumented source code      exclusive_directives[i] = false;
   3. original source code  
   */  
   
   Stream * instrumented = Stream_new(0);  
   instrument_statement(node, instrumented, 0);  
   
   /* write line number info to the output */  
   Stream_write_string(output, "/* automatically generated by JSCoverage - do not edit */\n");  
   Stream_write_string(output, "if (! top._$jscoverage) {\n  top._$jscoverage = {};\n}\n");  
   Stream_write_string(output, "var _$jscoverage = top._$jscoverage;\n");  
   Stream_printf(output, "if (! _$jscoverage['%s']) {\n", file_id);  
   Stream_printf(output, "  _$jscoverage['%s'] = [];\n", file_id);  
   for (int i = 0; i < num_lines; i++) {  
     if (lines[i]) {  
       Stream_printf(output, "  _$jscoverage['%s'][%d] = 0;\n", file_id, i + 1);  
     }  
1279    }    }
   Stream_write_string(output, "}\n");  
   free(lines);  
   lines = NULL;  
1280    
   /* copy the instrumented source code to the output */  
   Stream_write(output, instrumented->data, instrumented->length);  
   
   /* conditionals */  
1281    bool has_conditionals = false;    bool has_conditionals = false;
1282      struct IfDirective * if_directives = NULL;
1283    size_t line_number = 0;    size_t line_number = 0;
1284    size_t i = 0;    size_t i = 0;
1285    while (i < num_characters) {    while (i < num_characters) {
1286        if (line_number == UINT16_MAX) {
1287          fatal("file %s contains more than 65,535 lines", file_id);
1288        }
1289      line_number++;      line_number++;
1290      size_t line_start = i;      size_t line_start = i;
1291      jschar c;      jschar c;
# Line 977  Line 1301 
1301          break;          break;
1302        default:        default:
1303          i++;          i++;
         break;  
1304        }        }
1305      }      }
1306      size_t line_end = i;      size_t line_end = i;
# Line 987  Line 1310 
1310          i++;          i++;
1311        }        }
1312      }      }
1313    
1314      if (characters_start_with(characters, line_start, line_end, "//#JSCOVERAGE_IF")) {      if (characters_start_with(characters, line_start, line_end, "//#JSCOVERAGE_IF")) {
1315        if (! has_conditionals) {        has_conditionals = true;
1316          has_conditionals = true;  
1317          Stream_printf(output, "_$jscoverage['%s'].conditionals = [];\n", file_id);        if (characters_are_white_space(characters, line_start + 16, line_end)) {
1318        }          exclusive_directives[line_number - 1] = true;
1319        Stream_write_string(output, "if (!(");        }
1320        for (size_t j = line_start + 16; j < line_end; j++) {        else {
1321          jschar c = characters[j];          struct IfDirective * if_directive = xnew(struct IfDirective, 1);
1322          if (c == '\t' || (32 <= c && c <= 126)) {          if_directive->condition_start = characters + line_start + 16;
1323            Stream_write_char(output, c);          if_directive->condition_end = characters + line_end;
1324          }          if_directive->start_line = line_number;
1325          else {          if_directive->end_line = 0;
1326            Stream_printf(output, "\\u%04x", c);          if_directive->next = if_directives;
1327          }          if_directives = if_directive;
1328        }        }
       Stream_write_string(output, ")) {\n");  
       Stream_printf(output, "  _$jscoverage['%s'].conditionals[%d] = ", file_id, line_number);  
1329      }      }
1330      else if (characters_start_with(characters, line_start, line_end, "//#JSCOVERAGE_ENDIF")) {      else if (characters_start_with(characters, line_start, line_end, "//#JSCOVERAGE_ENDIF")) {
1331        Stream_printf(output, "%d;\n", line_number);        for (struct IfDirective * p = if_directives; p != NULL; p = p->next) {
1332        Stream_printf(output, "}\n");          if (p->end_line == 0) {
1333              p->end_line = line_number;
1334              break;
1335            }
1336          }
1337        }
1338      }
1339    
1340      /*
1341      An instrumented JavaScript file has 4 sections:
1342      1. initialization
1343      2. instrumented source code
1344      3. conditionals
1345      4. original source code
1346      */
1347    
1348      Stream * instrumented = Stream_new(0);
1349      instrument_statement(node, instrumented, 0, false);
1350      js_FinishParseContext(context, &parse_context);
1351    
1352      /* write line number info to the output */
1353      Stream_write_string(output, "/* automatically generated by JSCoverage - do not edit */\n");
1354      if (jscoverage_mozilla) {
1355        Stream_write_string(output, "try {\n");
1356        Stream_write_string(output, "  Components.utils.import('resource://gre/modules/jscoverage.jsm');\n");
1357        Stream_printf(output, "  dump('%s: successfully imported jscoverage module\\n');\n", id);
1358        Stream_write_string(output, "}\n");
1359        Stream_write_string(output, "catch (e) {\n");
1360        Stream_write_string(output, "  _$jscoverage = {};\n");
1361        Stream_printf(output, "  dump('%s: failed to import jscoverage module - coverage not available for this file\\n');\n", id);
1362        Stream_write_string(output, "}\n");
1363      }
1364      else {
1365        Stream_write_string(output, "if (! top._$jscoverage) {\n  top._$jscoverage = {};\n}\n");
1366        Stream_write_string(output, "var _$jscoverage = top._$jscoverage;\n");
1367      }
1368      Stream_printf(output, "if (! _$jscoverage['%s']) {\n", file_id);
1369      Stream_printf(output, "  _$jscoverage['%s'] = [];\n", file_id);
1370      for (int i = 0; i < num_lines; i++) {
1371        if (lines[i]) {
1372          Stream_printf(output, "  _$jscoverage['%s'][%d] = 0;\n", file_id, i + 1);
1373      }      }
1374    }    }
1375      Stream_write_string(output, "}\n");
1376      free(lines);
1377      lines = NULL;
1378      free(exclusive_directives);
1379      exclusive_directives = NULL;
1380    
1381      /* conditionals */
1382      if (has_conditionals) {
1383        Stream_printf(output, "_$jscoverage['%s'].conditionals = [];\n", file_id);
1384      }
1385    
1386      /* copy the instrumented source code to the output */
1387      Stream_write(output, instrumented->data, instrumented->length);
1388    
1389      /* conditionals */
1390      for (struct IfDirective * if_directive = if_directives; if_directive != NULL; if_directive = if_directive->next) {
1391        Stream_write_string(output, "if (!(");
1392        print_javascript(if_directive->condition_start, if_directive->condition_end - if_directive->condition_start, output);
1393        Stream_write_string(output, ")) {\n");
1394        Stream_printf(output, "  _$jscoverage['%s'].conditionals[%d] = %d;\n", file_id, if_directive->start_line, if_directive->end_line);
1395        Stream_write_string(output, "}\n");
1396      }
1397    
1398      /* free */
1399      while (if_directives != NULL) {
1400        struct IfDirective * if_directive = if_directives;
1401        if_directives = if_directives->next;
1402        free(if_directive);
1403      }
1404    
1405    /* copy the original source to the output */    /* copy the original source to the output */
1406    Stream_printf(output, "_$jscoverage['%s'].source = ", file_id);    Stream_printf(output, "_$jscoverage['%s'].source = ", file_id);
# Line 1049  Line 1440 
1440            /* line feed (new line) */            /* line feed (new line) */
1441            done = true;            done = true;
1442            break;            break;
1443            /* IE doesn't support this */
1444            /*
1445          case 0xb:          case 0xb:
           /* vertical tab */  
1446            Stream_write_string(output, "\\v");            Stream_write_string(output, "\\v");
1447            break;            break;
1448            */
1449          case 0xc:          case 0xc:
1450            /* form feed */            /* form feed */
1451            Stream_write_string(output, "\\f");            Stream_write_string(output, "\\f");
# Line 1107  Line 1500 
1500            /* line feed (new line) */            /* line feed (new line) */
1501            done = true;            done = true;
1502            break;            break;
1503            /* IE doesn't support this */
1504            /*
1505          case 0xb:          case 0xb:
           /* vertical tab */  
1506            Stream_write_string(output, "\\v");            Stream_write_string(output, "\\v");
1507            break;            break;
1508            */
1509          case 0xc:          case 0xc:
1510            /* form feed */            /* form feed */
1511            Stream_write_string(output, "\\f");            Stream_write_string(output, "\\f");
# Line 1237  Line 1632 
1632  }  }
1633    
1634  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) {
1635      int result = 0;
1636    
1637    jschar * base = js_InflateString(context, (char *) json, &length);    jschar * base = js_InflateString(context, (char *) json, &length);
1638    if (base == NULL) {    if (base == NULL) {
1639      fatal("out of memory");      fatal("out of memory");
# Line 1249  Line 1646 
1646    
1647    JS_free(context, base);    JS_free(context, base);
1648    
1649    JSTokenStream * token_stream = js_NewTokenStream(context, parenthesized_json, length + 2, NULL, 1, NULL);    JSParseContext parse_context;
1650    if (token_stream == NULL) {    if (! js_InitParseContext(context, &parse_context, NULL, NULL, parenthesized_json, length + 2, NULL, NULL, 1)) {
1651      fatal("cannot create token stream");      free(parenthesized_json);
1652        return -1;
1653    }    }
1654      JSParseNode * root = js_ParseScript(context, global, &parse_context);
   JSParseNode * root = js_ParseTokenStream(context, global, token_stream);  
1655    free(parenthesized_json);    free(parenthesized_json);
1656    if (root == NULL) {    if (root == NULL) {
1657      return -1;      result = -1;
1658        goto done;
1659    }    }
1660    
1661    /* root node must be TOK_LC */    /* root node must be TOK_LC */
1662    if (root->pn_type != TOK_LC) {    if (root->pn_type != TOK_LC) {
1663      return -1;      result = -1;
1664        goto done;
1665    }    }
1666    JSParseNode * semi = root->pn_u.list.head;    JSParseNode * semi = root->pn_u.list.head;
1667    
1668    /* 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 */
1669    if (semi->pn_type != TOK_SEMI || semi->pn_next != NULL) {    if (semi->pn_type != TOK_SEMI || semi->pn_next != NULL) {
1670      return -1;      result = -1;
1671        goto done;
1672    }    }
1673    JSParseNode * parenthesized = semi->pn_kid;    JSParseNode * parenthesized = semi->pn_kid;
1674    
1675    /* this must be a parenthesized expression */    /* this must be a parenthesized expression */
1676    if (parenthesized->pn_type != TOK_RP) {    if (parenthesized->pn_type != TOK_RP) {
1677      return -1;      result = -1;
1678        goto done;
1679    }    }
1680    JSParseNode * object = parenthesized->pn_kid;    JSParseNode * object = parenthesized->pn_kid;
1681    
1682    /* this must be an object literal */    /* this must be an object literal */
1683    if (object->pn_type != TOK_RC) {    if (object->pn_type != TOK_RC) {
1684      return -1;      result = -1;
1685        goto done;
1686    }    }
1687    
1688    for (JSParseNode * p = object->pn_head; p != NULL; p = p->pn_next) {    for (JSParseNode * p = object->pn_head; p != NULL; p = p->pn_next) {
1689      /* every element of this list must be TOK_COLON */      /* every element of this list must be TOK_COLON */
1690      if (p->pn_type != TOK_COLON) {      if (p->pn_type != TOK_COLON) {
1691        return -1;        result = -1;
1692          goto done;
1693      }      }
1694    
1695      /* the key must be a string representing the file */      /* the key must be a string representing the file */
1696      JSParseNode * key = p->pn_left;      JSParseNode * key = p->pn_left;
1697      if (key->pn_type != TOK_STRING || ! ATOM_IS_STRING(key->pn_atom)) {      if (key->pn_type != TOK_STRING || ! ATOM_IS_STRING(key->pn_atom)) {
1698        return -1;        result = -1;
1699          goto done;
1700      }      }
1701      char * id_bytes = JS_GetStringBytes(ATOM_TO_STRING(key->pn_atom));      char * id_bytes = JS_GetStringBytes(ATOM_TO_STRING(key->pn_atom));
1702    
1703      /* the value must be an object literal OR an array */      /* the value must be an object literal OR an array */
1704      JSParseNode * value = p->pn_right;      JSParseNode * value = p->pn_right;
1705      if (! (value->pn_type == TOK_RC || value->pn_type == TOK_RB)) {      if (! (value->pn_type == TOK_RC || value->pn_type == TOK_RB)) {
1706        return -1;        result = -1;
1707          goto done;
1708      }      }
1709    
1710      JSParseNode * array = NULL;      JSParseNode * array = NULL;
# Line 1311  Line 1716 
1716      else if (value->pn_type == TOK_RC) {      else if (value->pn_type == TOK_RC) {
1717        /* an object literal */        /* an object literal */
1718        if (value->pn_count != 2) {        if (value->pn_count != 2) {
1719          return -1;          result = -1;
1720            goto done;
1721        }        }
1722        for (JSParseNode * element = value->pn_head; element != NULL; element = element->pn_next) {        for (JSParseNode * element = value->pn_head; element != NULL; element = element->pn_next) {
1723          if (element->pn_type != TOK_COLON) {          if (element->pn_type != TOK_COLON) {
1724            return -1;            result = -1;
1725              goto done;
1726          }          }
1727          JSParseNode * left = element->pn_left;          JSParseNode * left = element->pn_left;
1728          if (left->pn_type != TOK_STRING || ! ATOM_IS_STRING(left->pn_atom)) {          if (left->pn_type != TOK_STRING || ! ATOM_IS_STRING(left->pn_atom)) {
1729            return -1;            result = -1;
1730              goto done;
1731          }          }
1732          const char * s = JS_GetStringBytes(ATOM_TO_STRING(left->pn_atom));          const char * s = JS_GetStringBytes(ATOM_TO_STRING(left->pn_atom));
1733          if (strcmp(s, "coverage") == 0) {          if (strcmp(s, "coverage") == 0) {
1734            array = element->pn_right;            array = element->pn_right;
1735            if (array->pn_type != TOK_RB) {            if (array->pn_type != TOK_RB) {
1736              return -1;              result = -1;
1737                goto done;
1738            }            }
1739          }          }
1740          else if (strcmp(s, "source") == 0) {          else if (strcmp(s, "source") == 0) {
1741            source = element->pn_right;            source = element->pn_right;
1742            if (source->pn_type != TOK_RB) {            if (source->pn_type != TOK_RB) {
1743              return -1;              result = -1;
1744                goto done;
1745            }            }
1746          }          }
1747          else {          else {
1748            return -1;            result = -1;
1749              goto done;
1750          }          }
1751        }        }
1752      }      }
1753      else {      else {
1754        return -1;        result = -1;
1755          goto done;
1756      }      }
1757    
1758      if (array == NULL) {      if (array == NULL) {
1759        return -1;        result = -1;
1760          goto done;
1761      }      }
1762    
1763      /* look up the file in the coverage table */      /* look up the file in the coverage table */
# Line 1356  Line 1769 
1769        file_coverage->id = id;        file_coverage->id = id;
1770        file_coverage->num_coverage_lines = array->pn_count;        file_coverage->num_coverage_lines = array->pn_count;
1771        file_coverage->coverage_lines = xnew(int, array->pn_count);        file_coverage->coverage_lines = xnew(int, array->pn_count);
1772        if (source == NULL) {        file_coverage->source_lines = NULL;
         file_coverage->source_lines = NULL;  
       }  
       else {  
         file_coverage->num_source_lines = source->pn_count;  
         file_coverage->source_lines = xnew(char *, source->pn_count);  
         uint32 i = 0;  
         for (JSParseNode * element = source->pn_head; element != NULL; element = element->pn_next, i++) {  
           if (element->pn_type != TOK_STRING) {  
             return -1;  
           }  
           file_coverage->source_lines[i] = xstrdup(JS_GetStringBytes(ATOM_TO_STRING(element->pn_atom)));  
         }  
         assert(i == source->pn_count);  
       }  
1773    
1774        /* set coverage for all lines */        /* set coverage for all lines */
1775        uint32 i = 0;        uint32 i = 0;
# Line 1382  Line 1781 
1781            file_coverage->coverage_lines[i] = -1;            file_coverage->coverage_lines[i] = -1;
1782          }          }
1783          else {          else {
1784            return -1;            result = -1;
1785              goto done;
1786          }          }
1787        }        }
1788        assert(i == array->pn_count);        assert(i == array->pn_count);
# Line 1398  Line 1798 
1798        /* sanity check */        /* sanity check */
1799        assert(strcmp(file_coverage->id, id_bytes) == 0);        assert(strcmp(file_coverage->id, id_bytes) == 0);
1800        if (file_coverage->num_coverage_lines != array->pn_count) {        if (file_coverage->num_coverage_lines != array->pn_count) {
1801          return -2;          result = -2;
1802            goto done;
1803        }        }
1804    
1805        /* merge the coverage */        /* merge the coverage */
# Line 1406  Line 1807 
1807        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++) {
1808          if (element->pn_type == TOK_NUMBER) {          if (element->pn_type == TOK_NUMBER) {
1809            if (file_coverage->coverage_lines[i] == -1) {            if (file_coverage->coverage_lines[i] == -1) {
1810              return -2;              result = -2;
1811                goto done;
1812            }            }
1813            file_coverage->coverage_lines[i] += (int) element->pn_dval;            file_coverage->coverage_lines[i] += (int) element->pn_dval;
1814          }          }
1815          else if (element->pn_type == TOK_PRIMARY && element->pn_op == JSOP_NULL) {          else if (element->pn_type == TOK_PRIMARY && element->pn_op == JSOP_NULL) {
1816            if (file_coverage->coverage_lines[i] != -1) {            if (file_coverage->coverage_lines[i] != -1) {
1817              return -2;              result = -2;
1818                goto done;
1819            }            }
1820          }          }
1821          else {          else {
1822            return -1;            result = -1;
1823              goto done;
1824          }          }
1825        }        }
1826        assert(i == array->pn_count);        assert(i == array->pn_count);
1827        }
1828    
1829        /* if this JSON file has source, use it */      /* if this JSON file has source, use it */
1830        if (file_coverage->source_lines == NULL && source != NULL) {      if (file_coverage->source_lines == NULL && source != NULL) {
1831          file_coverage->num_source_lines = source->pn_count;        file_coverage->num_source_lines = source->pn_count;
1832          file_coverage->source_lines = xnew(char *, source->pn_count);        file_coverage->source_lines = xnew(char *, source->pn_count);
1833          uint32 i = 0;        uint32 i = 0;
1834          for (JSParseNode * element = source->pn_head; element != NULL; element = element->pn_next, i++) {        for (JSParseNode * element = source->pn_head; element != NULL; element = element->pn_next, i++) {
1835            if (element->pn_type != TOK_STRING) {          if (element->pn_type != TOK_STRING) {
1836              return -1;            result = -1;
1837            }            goto done;
           file_coverage->source_lines[i] = xstrdup(JS_GetStringBytes(ATOM_TO_STRING(element->pn_atom)));  
1838          }          }
1839          assert(i == source->pn_count);          file_coverage->source_lines[i] = xstrdup(JS_GetStringBytes(ATOM_TO_STRING(element->pn_atom)));
1840        }        }
1841          assert(i == source->pn_count);
1842      }      }
1843    }    }
1844    
1845    return 0;  done:
1846      js_FinishParseContext(context, &parse_context);
1847      return result;
1848  }  }

Legend:
Removed from v.214  
changed lines
  Added in v.379

  ViewVC Help
Powered by ViewVC 1.1.24