/[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 240 by siliconforks, Fri Oct 3 23:51:32 2008 UTC revision 376 by siliconforks, Tue Oct 28 05:30:23 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 48  Line 52 
52    struct IfDirective * next;    struct IfDirective * next;
53  };  };
54    
55    bool jscoverage_mozilla = false;
56    
57  static bool * exclusive_directives = NULL;  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 62  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 73  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 88  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 122  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 171  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 181  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 216  Line 269 
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, bool is_jscoverage_if);  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_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          instrument_expression(expression, f);
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, false);    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 288  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 315  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 346  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 410  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 458  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 must be parenthesized */
659        if (node->pn_expr->pn_type == TOK_NUMBER) {
660          Stream_write_char(f, '(');
661          instrument_expression(node->pn_expr, f);
662          Stream_write_char(f, ')');
663        }
664        else {
665          instrument_expression(node->pn_expr, f);
666        }
667      /*      /*
668      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'
669      contains illegal characters, we have to use the subscript syntax instead of      contains illegal characters, we have to use the subscript syntax instead of
670      the dot syntax.      the dot syntax.
671      */      */
     instrument_expression(node->pn_expr, f);  
672      assert(ATOM_IS_STRING(node->pn_atom));      assert(ATOM_IS_STRING(node->pn_atom));
673      {      {
674        JSString * s = ATOM_TO_STRING(node->pn_atom);        JSString * s = ATOM_TO_STRING(node->pn_atom);
675        /* XXX - semantics changed in 1.7 */        bool must_quote;
676        if (! ATOM_KEYWORD(node->pn_atom) && js_IsIdentifier(s)) {        if (JSSTRING_LENGTH(s) == 0) {
677          Stream_write_char(f, '.');          must_quote = true;
678          print_string_atom(node->pn_atom, f);        }
679          else if (js_CheckKeyword(JSSTRING_CHARS(s), JSSTRING_LENGTH(s)) != TOK_EOF) {
680            must_quote = true;
681          }
682          else if (! js_IsIdentifier(s)) {
683            must_quote = true;
684        }        }
685        else {        else {
686            must_quote = false;
687          }
688          if (must_quote) {
689          Stream_write_char(f, '[');          Stream_write_char(f, '[');
690          print_quoted_string_atom(node->pn_atom, f);          print_quoted_string_atom(node->pn_atom, f);
691          Stream_write_char(f, ']');          Stream_write_char(f, ']');
692        }        }
693          else {
694            Stream_write_char(f, '.');
695            print_string_atom(node->pn_atom, f);
696          }
697      }      }
698      break;      break;
699    case TOK_LB:    case TOK_LB:
# Line 507  Line 724 
724    case TOK_RC:    case TOK_RC:
725      Stream_write_char(f, '{');      Stream_write_char(f, '{');
726      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) {
727        assert(p->pn_type == TOK_COLON);        if (p->pn_type != TOK_COLON) {
728            fatal_source(file_id, p->pn_pos.begin.lineno, "unsupported node type (%d)", p->pn_type);
729          }
730        if (p != node->pn_head) {        if (p != node->pn_head) {
731          Stream_write_string(f, ", ");          Stream_write_string(f, ", ");
732        }        }
# Line 515  Line 734 
734        /* check whether this is a getter or setter */        /* check whether this is a getter or setter */
735        switch (p->pn_op) {        switch (p->pn_op) {
736        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;  
737        case JSOP_SETTER:        case JSOP_SETTER:
738          Stream_write_string(f, "set ");          if (p->pn_op == JSOP_GETTER) {
739              Stream_write_string(f, "get ");
740            }
741            else {
742              Stream_write_string(f, "set ");
743            }
744          instrument_expression(p->pn_left, f);          instrument_expression(p->pn_left, f);
745            if (p->pn_right->pn_type != TOK_FUNCTION) {
746              fatal_source(file_id, p->pn_pos.begin.lineno, "expected function");
747            }
748          instrument_function(p->pn_right, f, 0, FUNCTION_GETTER_OR_SETTER);          instrument_function(p->pn_right, f, 0, FUNCTION_GETTER_OR_SETTER);
749          break;          break;
750        default:        default:
# Line 544  Line 767 
767    case TOK_STRING:    case TOK_STRING:
768      print_quoted_string_atom(node->pn_atom, f);      print_quoted_string_atom(node->pn_atom, f);
769      break;      break;
770    case TOK_OBJECT:    case TOK_REGEXP:
771      switch (node->pn_op) {      assert(node->pn_op == JSOP_REGEXP);
772      case JSOP_OBJECT:      {
773        /* I assume this is JSOP_REGEXP */        JSObject * object = node->pn_pob->object;
774        abort();        jsval result;
775        break;        js_regexp_toString(context, object, &result);
776      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;  
777      }      }
778      break;      break;
779    case TOK_NUMBER:    case TOK_NUMBER:
# Line 607  Line 819 
819      Stream_write_string(f, " in ");      Stream_write_string(f, " in ");
820      instrument_expression(node->pn_right, f);      instrument_expression(node->pn_right, f);
821      break;      break;
822    default:    case TOK_LEXICALSCOPE:
823      fatal("unsupported node type in file %s: %d", file_id, node->pn_type);      assert(node->pn_arity == PN_NAME);
824    }      assert(node->pn_expr->pn_type == TOK_LET);
825  }      assert(node->pn_expr->pn_arity == PN_BINARY);
826        Stream_write_string(f, "let(");
827  static void instrument_var_statement(JSParseNode * node, Stream * f, int indent) {      assert(node->pn_expr->pn_left->pn_type == TOK_LP);
828    assert(node->pn_arity == PN_LIST);      assert(node->pn_expr->pn_left->pn_arity == PN_LIST);
829    Stream_printf(f, "%*s", indent, "");      instrument_declarations(node->pn_expr->pn_left, f);
830    Stream_write_string(f, "var ");      Stream_write_string(f, ") ");
831    for (struct JSParseNode * p = node->pn_u.list.head; p != NULL; p = p->pn_next) {      instrument_expression(node->pn_expr->pn_right, f);
832      assert(p->pn_type == TOK_NAME);      break;
833      assert(p->pn_arity == PN_NAME);    case TOK_YIELD:
834      if (p != node->pn_head) {      assert(node->pn_arity == PN_UNARY);
835        Stream_write_string(f, ", ");      Stream_write_string(f, "yield");
836        if (node->pn_kid != NULL) {
837          Stream_write_char(f, ' ');
838          instrument_expression(node->pn_kid, f);
839      }      }
840      print_string_atom(p->pn_atom, f);      break;
841      if (p->pn_expr != NULL) {    case TOK_ARRAYCOMP:
842        Stream_write_string(f, " = ");      assert(node->pn_arity == PN_LIST);
843        instrument_expression(p->pn_expr, f);      {
844          JSParseNode * block_node;
845          switch (node->pn_count) {
846          case 1:
847            block_node = node->pn_head;
848            break;
849          case 2:
850            block_node = node->pn_head->pn_next;
851            break;
852          default:
853            abort();
854            break;
855          }
856          Stream_write_char(f, '[');
857          output_array_comprehension_or_generator_expression(block_node, f);
858          Stream_write_char(f, ']');
859      }      }
860        break;
861      case TOK_VAR:
862        assert(node->pn_arity == PN_LIST);
863        Stream_write_string(f, "var ");
864        instrument_declarations(node, f);
865        break;
866      case TOK_LET:
867        assert(node->pn_arity == PN_LIST);
868        Stream_write_string(f, "let ");
869        instrument_declarations(node, f);
870        break;
871      default:
872        fatal_source(file_id, node->pn_pos.begin.lineno, "unsupported node type (%d)", node->pn_type);
873    }    }
874  }  }
875    
# Line 655  Line 898 
898      uint16_t line = node->pn_pos.begin.lineno;      uint16_t line = node->pn_pos.begin.lineno;
899      if (! is_jscoverage_if) {      if (! is_jscoverage_if) {
900        if (line > num_lines) {        if (line > num_lines) {
901          fatal("%s: script contains more than 65,535 lines", file_id);          fatal("file %s contains more than 65,535 lines", file_id);
902        }        }
903        if (line >= 2 && exclusive_directives[line - 2]) {        if (line >= 2 && exclusive_directives[line - 2]) {
904          is_jscoverage_if = true;          is_jscoverage_if = true;
# Line 703  Line 946 
946      Stream_write_string(f, "switch (");      Stream_write_string(f, "switch (");
947      instrument_expression(node->pn_left, f);      instrument_expression(node->pn_left, f);
948      Stream_write_string(f, ") {\n");      Stream_write_string(f, ") {\n");
949      for (struct JSParseNode * p = node->pn_right->pn_head; p != NULL; p = p->pn_next) {      {
950        Stream_printf(f, "%*s", indent, "");        JSParseNode * list = node->pn_right;
951        switch (p->pn_type) {        if (list->pn_type == TOK_LEXICALSCOPE) {
952        case TOK_CASE:          list = list->pn_expr;
953          Stream_write_string(f, "case ");        }
954          instrument_expression(p->pn_left, f);        for (struct JSParseNode * p = list->pn_head; p != NULL; p = p->pn_next) {
955          Stream_write_string(f, ":\n");          Stream_printf(f, "%*s", indent, "");
956          break;          switch (p->pn_type) {
957        case TOK_DEFAULT:          case TOK_CASE:
958          Stream_write_string(f, "default:\n");            Stream_write_string(f, "case ");
959          break;            instrument_expression(p->pn_left, f);
960        default:            Stream_write_string(f, ":\n");
961          abort();            break;
962          break;          case TOK_DEFAULT:
963              Stream_write_string(f, "default:\n");
964              break;
965            default:
966              abort();
967              break;
968            }
969            instrument_statement(p->pn_right, f, indent + 2, false);
970        }        }
       instrument_statement(p->pn_right, f, indent + 2, false);  
971      }      }
972      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
973      Stream_write_string(f, "}\n");      Stream_write_string(f, "}\n");
# Line 750  Line 999 
999    case TOK_FOR:    case TOK_FOR:
1000      assert(node->pn_arity == PN_BINARY);      assert(node->pn_arity == PN_BINARY);
1001      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
     Stream_write_string(f, "for (");  
1002      switch (node->pn_left->pn_type) {      switch (node->pn_left->pn_type) {
1003      case TOK_IN:      case TOK_IN:
1004        /* for/in */        /* for/in */
1005        assert(node->pn_left->pn_arity == PN_BINARY);        assert(node->pn_left->pn_arity == PN_BINARY);
1006        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);  
1007        break;        break;
1008      case TOK_RESERVED:      case TOK_RESERVED:
1009        /* for (;;) */        /* for (;;) */
1010        assert(node->pn_left->pn_arity == PN_TERNARY);        assert(node->pn_left->pn_arity == PN_TERNARY);
1011          Stream_write_string(f, "for (");
1012        if (node->pn_left->pn_kid1) {        if (node->pn_left->pn_kid1) {
1013          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);  
         }  
1014        }        }
1015        Stream_write_string(f, ";");        Stream_write_string(f, ";");
1016        if (node->pn_left->pn_kid2) {        if (node->pn_left->pn_kid2) {
# Line 797  Line 1022 
1022          Stream_write_char(f, ' ');          Stream_write_char(f, ' ');
1023          instrument_expression(node->pn_left->pn_kid3, f);          instrument_expression(node->pn_left->pn_kid3, f);
1024        }        }
1025          Stream_write_char(f, ')');
1026        break;        break;
1027      default:      default:
1028        abort();        abort();
1029        break;        break;
1030      }      }
1031      Stream_write_string(f, ") {\n");      Stream_write_string(f, " {\n");
1032      instrument_statement(node->pn_right, f, indent + 2, false);      instrument_statement(node->pn_right, f, indent + 2, false);
1033      Stream_write_string(f, "}\n");      Stream_write_string(f, "}\n");
1034      break;      break;
# Line 819  Line 1045 
1045      instrument_statement(node->pn_kid1, f, indent + 2, false);      instrument_statement(node->pn_kid1, f, indent + 2, false);
1046      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
1047      Stream_write_string(f, "}\n");      Stream_write_string(f, "}\n");
1048      {      if (node->pn_kid2) {
1049        for (JSParseNode * catch = node->pn_kid2; catch != NULL; catch = catch->pn_kid2) {        assert(node->pn_kid2->pn_type == TOK_RESERVED);
1050          for (JSParseNode * scope = node->pn_kid2->pn_head; scope != NULL; scope = scope->pn_next) {
1051            assert(scope->pn_type == TOK_LEXICALSCOPE);
1052            JSParseNode * catch = scope->pn_expr;
1053          assert(catch->pn_type == TOK_CATCH);          assert(catch->pn_type == TOK_CATCH);
1054          Stream_printf(f, "%*s", indent, "");          Stream_printf(f, "%*s", indent, "");
1055          Stream_write_string(f, "catch (");          Stream_write_string(f, "catch (");
1056            /* this may not be a name - destructuring assignment */
1057            /*
1058          assert(catch->pn_kid1->pn_arity == PN_NAME);          assert(catch->pn_kid1->pn_arity == PN_NAME);
1059          print_string_atom(catch->pn_kid1->pn_atom, f);          print_string_atom(catch->pn_kid1->pn_atom, f);
1060          if (catch->pn_kid1->pn_expr) {          */
1061            instrument_expression(catch->pn_kid1, f);
1062            if (catch->pn_kid2) {
1063            Stream_write_string(f, " if ");            Stream_write_string(f, " if ");
1064            instrument_expression(catch->pn_kid1->pn_expr, f);            instrument_expression(catch->pn_kid2, f);
1065          }          }
1066          Stream_write_string(f, ") {\n");          Stream_write_string(f, ") {\n");
1067          instrument_statement(catch->pn_kid3, f, indent + 2, false);          instrument_statement(catch->pn_kid3, f, indent + 2, false);
# Line 870  Line 1103 
1103      Stream_write_string(f, "}\n");      Stream_write_string(f, "}\n");
1104      break;      break;
1105    case TOK_VAR:    case TOK_VAR:
1106      instrument_var_statement(node, f, indent);      Stream_printf(f, "%*s", indent, "");
1107        instrument_expression(node, f);
1108      Stream_write_string(f, ";\n");      Stream_write_string(f, ";\n");
1109      break;      break;
1110    case TOK_RETURN:    case TOK_RETURN:
# Line 892  Line 1126 
1126      Stream_write_string(f, ";\n");      Stream_write_string(f, ";\n");
1127      break;      break;
1128    case TOK_COLON:    case TOK_COLON:
1129      {
1130      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 ...  
     */  
1131      Stream_printf(f, "%*s", indent < 2? 0: indent - 2, "");      Stream_printf(f, "%*s", indent < 2? 0: indent - 2, "");
1132      print_string_atom(node->pn_atom, f);      print_string_atom(node->pn_atom, f);
1133      Stream_write_string(f, ":\n");      Stream_write_string(f, ":\n");
1134      /*      JSParseNode * labelled = node->pn_expr;
1135      ... use output_statement instead of instrument_statement.      if (labelled->pn_type == TOK_LEXICALSCOPE) {
1136      */        labelled = labelled->pn_expr;
1137      output_statement(node->pn_expr, f, indent, false);      }
1138        if (labelled->pn_type == TOK_LC) {
1139          /* labelled block */
1140          Stream_printf(f, "%*s", indent, "");
1141          Stream_write_string(f, "{\n");
1142          instrument_statement(labelled, f, indent + 2, false);
1143          Stream_printf(f, "%*s", indent, "");
1144          Stream_write_string(f, "}\n");
1145        }
1146        else {
1147          /*
1148          This one is tricky: can't output instrumentation between the label and the
1149          statement it's supposed to label, so use output_statement instead of
1150          instrument_statement.
1151          */
1152          output_statement(labelled, f, indent, false);
1153        }
1154        break;
1155      }
1156      case TOK_LEXICALSCOPE:
1157        /* let statement */
1158        assert(node->pn_arity == PN_NAME);
1159        switch (node->pn_expr->pn_type) {
1160        case TOK_LET:
1161          /* let statement */
1162          assert(node->pn_expr->pn_arity == PN_BINARY);
1163          instrument_statement(node->pn_expr, f, indent, false);
1164          break;
1165        case TOK_LC:
1166          /* block */
1167          Stream_printf(f, "%*s", indent, "");
1168          Stream_write_string(f, "{\n");
1169          instrument_statement(node->pn_expr, f, indent + 2, false);
1170          Stream_printf(f, "%*s", indent, "");
1171          Stream_write_string(f, "}\n");
1172          break;
1173        case TOK_FOR:
1174          instrument_statement(node->pn_expr, f, indent, false);
1175          break;
1176        default:
1177          abort();
1178          break;
1179        }
1180        break;
1181      case TOK_LET:
1182        switch (node->pn_arity) {
1183        case PN_BINARY:
1184          /* let statement */
1185          Stream_printf(f, "%*s", indent, "");
1186          Stream_write_string(f, "let (");
1187          assert(node->pn_left->pn_type == TOK_LP);
1188          assert(node->pn_left->pn_arity == PN_LIST);
1189          instrument_declarations(node->pn_left, f);
1190          Stream_write_string(f, ") {\n");
1191          instrument_statement(node->pn_right, f, indent + 2, false);
1192          Stream_printf(f, "%*s", indent, "");
1193          Stream_write_string(f, "}\n");
1194          break;
1195        case PN_LIST:
1196          /* let definition */
1197          Stream_printf(f, "%*s", indent, "");
1198          instrument_expression(node, f);
1199          Stream_write_string(f, ";\n");
1200          break;
1201        default:
1202          abort();
1203          break;
1204        }
1205        break;
1206      case TOK_DEBUGGER:
1207        Stream_printf(f, "%*s", indent, "");
1208        Stream_write_string(f, "debugger;\n");
1209      break;      break;
1210    default:    default:
1211      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);
1212    }    }
1213  }  }
1214    
# Line 916  Line 1218 
1218  TOK_EXPORT, TOK_IMPORT are not handled.  TOK_EXPORT, TOK_IMPORT are not handled.
1219  */  */
1220  static void instrument_statement(JSParseNode * node, Stream * f, int indent, bool is_jscoverage_if) {  static void instrument_statement(JSParseNode * node, Stream * f, int indent, bool is_jscoverage_if) {
1221    if (node->pn_type != TOK_LC) {    if (node->pn_type != TOK_LC && node->pn_type != TOK_LEXICALSCOPE) {
1222      uint16_t line = node->pn_pos.begin.lineno;      uint16_t line = node->pn_pos.begin.lineno;
1223      if (line > num_lines) {      if (line > num_lines) {
1224        fatal("%s: script contains more than 65,535 lines", file_id);        fatal("file %s contains more than 65,535 lines", file_id);
1225      }      }
1226    
1227      /* the root node has line number 0 */      /* the root node has line number 0 */
# Line 966  Line 1268 
1268    return true;    return true;
1269  }  }
1270    
1271    static void error_reporter(JSContext * context, const char * message, JSErrorReport * report) {
1272      warn_source(file_id, report->lineno, "%s", message);
1273    }
1274    
1275  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) {
1276    file_id = id;    file_id = id;
1277    
   /* 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);  
   }  
   
1278    /* parse the javascript */    /* parse the javascript */
1279    JSParseNode * node = js_ParseTokenStream(context, global, token_stream);    JSParseContext parse_context;
1280      if (! js_InitParseContext(context, &parse_context, NULL, NULL, characters, num_characters, NULL, NULL, 1)) {
1281        fatal("cannot create token stream from file %s", file_id);
1282      }
1283      JSErrorReporter old_error_reporter = JS_SetErrorReporter(context, error_reporter);
1284      JSParseNode * node = js_ParseScript(context, global, &parse_context);
1285    if (node == NULL) {    if (node == NULL) {
1286      fatal("parse error in file: %s", file_id);      js_ReportUncaughtException(context);
1287        fatal("parse error in file %s", file_id);
1288    }    }
1289      JS_SetErrorReporter(context, old_error_reporter);
1290    num_lines = node->pn_pos.end.lineno;    num_lines = node->pn_pos.end.lineno;
1291    lines = xmalloc(num_lines);    lines = xmalloc(num_lines);
1292    for (unsigned int i = 0; i < num_lines; i++) {    for (unsigned int i = 0; i < num_lines; i++) {
# Line 998  Line 1305 
1305    size_t i = 0;    size_t i = 0;
1306    while (i < num_characters) {    while (i < num_characters) {
1307      if (line_number == UINT16_MAX) {      if (line_number == UINT16_MAX) {
1308        fatal("%s: script has more than 65,535 lines", file_id);        fatal("file %s contains more than 65,535 lines", file_id);
1309      }      }
1310      line_number++;      line_number++;
1311      size_t line_start = i;      size_t line_start = i;
# Line 1061  Line 1368 
1368    
1369    Stream * instrumented = Stream_new(0);    Stream * instrumented = Stream_new(0);
1370    instrument_statement(node, instrumented, 0, false);    instrument_statement(node, instrumented, 0, false);
1371      js_FinishParseContext(context, &parse_context);
1372    
1373    /* write line number info to the output */    /* write line number info to the output */
1374    Stream_write_string(output, "/* automatically generated by JSCoverage - do not edit */\n");    Stream_write_string(output, "/* automatically generated by JSCoverage - do not edit */\n");
1375    Stream_write_string(output, "if (! top._$jscoverage) {\n  top._$jscoverage = {};\n}\n");    if (jscoverage_mozilla) {
1376    Stream_write_string(output, "var _$jscoverage = top._$jscoverage;\n");      Stream_write_string(output, "try {\n");
1377        Stream_write_string(output, "  Components.utils.import('resource://gre/modules/jscoverage.jsm');\n");
1378        Stream_printf(output, "  dump('%s: successfully imported jscoverage module\\n');\n", id);
1379        Stream_write_string(output, "}\n");
1380        Stream_write_string(output, "catch (e) {\n");
1381        Stream_write_string(output, "  _$jscoverage = {};\n");
1382        Stream_printf(output, "  dump('%s: failed to import jscoverage module - coverage not available for this file\\n');\n", id);
1383        Stream_write_string(output, "}\n");
1384      }
1385      else {
1386        Stream_write_string(output, "if (! top._$jscoverage) {\n  top._$jscoverage = {};\n}\n");
1387        Stream_write_string(output, "var _$jscoverage = top._$jscoverage;\n");
1388      }
1389    Stream_printf(output, "if (! _$jscoverage['%s']) {\n", file_id);    Stream_printf(output, "if (! _$jscoverage['%s']) {\n", file_id);
1390    Stream_printf(output, "  _$jscoverage['%s'] = [];\n", file_id);    Stream_printf(output, "  _$jscoverage['%s'] = [];\n", file_id);
1391    for (int i = 0; i < num_lines; i++) {    for (int i = 0; i < num_lines; i++) {
# Line 1090  Line 1410 
1410    /* conditionals */    /* conditionals */
1411    for (struct IfDirective * if_directive = if_directives; if_directive != NULL; if_directive = if_directive->next) {    for (struct IfDirective * if_directive = if_directives; if_directive != NULL; if_directive = if_directive->next) {
1412      Stream_write_string(output, "if (!(");      Stream_write_string(output, "if (!(");
1413      for (const jschar * p = if_directive->condition_start; p < if_directive->condition_end; p++) {      print_javascript(if_directive->condition_start, if_directive->condition_end - if_directive->condition_start, output);
       jschar c = *p;  
       if (c == '\t' || (32 <= c && c <= 126)) {  
         Stream_write_char(output, c);  
       }  
       else {  
         Stream_printf(output, "\\u%04x", c);  
       }  
     }  
1414      Stream_write_string(output, ")) {\n");      Stream_write_string(output, ")) {\n");
1415      Stream_printf(output, "  _$jscoverage['%s'].conditionals[%d] = %d;\n", file_id, if_directive->start_line, if_directive->end_line);      Stream_printf(output, "  _$jscoverage['%s'].conditionals[%d] = %d;\n", file_id, if_directive->start_line, if_directive->end_line);
1416      Stream_write_string(output, "}\n");      Stream_write_string(output, "}\n");
# Line 1149  Line 1461 
1461            /* line feed (new line) */            /* line feed (new line) */
1462            done = true;            done = true;
1463            break;            break;
1464            /* IE doesn't support this */
1465            /*
1466          case 0xb:          case 0xb:
           /* vertical tab */  
1467            Stream_write_string(output, "\\v");            Stream_write_string(output, "\\v");
1468            break;            break;
1469            */
1470          case 0xc:          case 0xc:
1471            /* form feed */            /* form feed */
1472            Stream_write_string(output, "\\f");            Stream_write_string(output, "\\f");
# Line 1207  Line 1521 
1521            /* line feed (new line) */            /* line feed (new line) */
1522            done = true;            done = true;
1523            break;            break;
1524            /* IE doesn't support this */
1525            /*
1526          case 0xb:          case 0xb:
           /* vertical tab */  
1527            Stream_write_string(output, "\\v");            Stream_write_string(output, "\\v");
1528            break;            break;
1529            */
1530          case 0xc:          case 0xc:
1531            /* form feed */            /* form feed */
1532            Stream_write_string(output, "\\f");            Stream_write_string(output, "\\f");
# Line 1337  Line 1653 
1653  }  }
1654    
1655  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) {
1656      int result = 0;
1657    
1658    jschar * base = js_InflateString(context, (char *) json, &length);    jschar * base = js_InflateString(context, (char *) json, &length);
1659    if (base == NULL) {    if (base == NULL) {
1660      fatal("out of memory");      fatal("out of memory");
# Line 1349  Line 1667 
1667    
1668    JS_free(context, base);    JS_free(context, base);
1669    
1670    JSTokenStream * token_stream = js_NewTokenStream(context, parenthesized_json, length + 2, NULL, 1, NULL);    JSParseContext parse_context;
1671    if (token_stream == NULL) {    if (! js_InitParseContext(context, &parse_context, NULL, NULL, parenthesized_json, length + 2, NULL, NULL, 1)) {
1672      fatal("cannot create token stream");      free(parenthesized_json);
1673        return -1;
1674    }    }
1675      JSParseNode * root = js_ParseScript(context, global, &parse_context);
   JSParseNode * root = js_ParseTokenStream(context, global, token_stream);  
1676    free(parenthesized_json);    free(parenthesized_json);
1677    if (root == NULL) {    if (root == NULL) {
1678      return -1;      result = -1;
1679        goto done;
1680    }    }
1681    
1682    /* root node must be TOK_LC */    /* root node must be TOK_LC */
1683    if (root->pn_type != TOK_LC) {    if (root->pn_type != TOK_LC) {
1684      return -1;      result = -1;
1685        goto done;
1686    }    }
1687    JSParseNode * semi = root->pn_u.list.head;    JSParseNode * semi = root->pn_u.list.head;
1688    
1689    /* 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 */
1690    if (semi->pn_type != TOK_SEMI || semi->pn_next != NULL) {    if (semi->pn_type != TOK_SEMI || semi->pn_next != NULL) {
1691      return -1;      result = -1;
1692        goto done;
1693    }    }
1694    JSParseNode * parenthesized = semi->pn_kid;    JSParseNode * parenthesized = semi->pn_kid;
1695    
1696    /* this must be a parenthesized expression */    /* this must be a parenthesized expression */
1697    if (parenthesized->pn_type != TOK_RP) {    if (parenthesized->pn_type != TOK_RP) {
1698      return -1;      result = -1;
1699        goto done;
1700    }    }
1701    JSParseNode * object = parenthesized->pn_kid;    JSParseNode * object = parenthesized->pn_kid;
1702    
1703    /* this must be an object literal */    /* this must be an object literal */
1704    if (object->pn_type != TOK_RC) {    if (object->pn_type != TOK_RC) {
1705      return -1;      result = -1;
1706        goto done;
1707    }    }
1708    
1709    for (JSParseNode * p = object->pn_head; p != NULL; p = p->pn_next) {    for (JSParseNode * p = object->pn_head; p != NULL; p = p->pn_next) {
1710      /* every element of this list must be TOK_COLON */      /* every element of this list must be TOK_COLON */
1711      if (p->pn_type != TOK_COLON) {      if (p->pn_type != TOK_COLON) {
1712        return -1;        result = -1;
1713          goto done;
1714      }      }
1715    
1716      /* the key must be a string representing the file */      /* the key must be a string representing the file */
1717      JSParseNode * key = p->pn_left;      JSParseNode * key = p->pn_left;
1718      if (key->pn_type != TOK_STRING || ! ATOM_IS_STRING(key->pn_atom)) {      if (key->pn_type != TOK_STRING || ! ATOM_IS_STRING(key->pn_atom)) {
1719        return -1;        result = -1;
1720          goto done;
1721      }      }
1722      char * id_bytes = JS_GetStringBytes(ATOM_TO_STRING(key->pn_atom));      char * id_bytes = JS_GetStringBytes(ATOM_TO_STRING(key->pn_atom));
1723    
1724      /* the value must be an object literal OR an array */      /* the value must be an object literal OR an array */
1725      JSParseNode * value = p->pn_right;      JSParseNode * value = p->pn_right;
1726      if (! (value->pn_type == TOK_RC || value->pn_type == TOK_RB)) {      if (! (value->pn_type == TOK_RC || value->pn_type == TOK_RB)) {
1727        return -1;        result = -1;
1728          goto done;
1729      }      }
1730    
1731      JSParseNode * array = NULL;      JSParseNode * array = NULL;
# Line 1411  Line 1737 
1737      else if (value->pn_type == TOK_RC) {      else if (value->pn_type == TOK_RC) {
1738        /* an object literal */        /* an object literal */
1739        if (value->pn_count != 2) {        if (value->pn_count != 2) {
1740          return -1;          result = -1;
1741            goto done;
1742        }        }
1743        for (JSParseNode * element = value->pn_head; element != NULL; element = element->pn_next) {        for (JSParseNode * element = value->pn_head; element != NULL; element = element->pn_next) {
1744          if (element->pn_type != TOK_COLON) {          if (element->pn_type != TOK_COLON) {
1745            return -1;            result = -1;
1746              goto done;
1747          }          }
1748          JSParseNode * left = element->pn_left;          JSParseNode * left = element->pn_left;
1749          if (left->pn_type != TOK_STRING || ! ATOM_IS_STRING(left->pn_atom)) {          if (left->pn_type != TOK_STRING || ! ATOM_IS_STRING(left->pn_atom)) {
1750            return -1;            result = -1;
1751              goto done;
1752          }          }
1753          const char * s = JS_GetStringBytes(ATOM_TO_STRING(left->pn_atom));          const char * s = JS_GetStringBytes(ATOM_TO_STRING(left->pn_atom));
1754          if (strcmp(s, "coverage") == 0) {          if (strcmp(s, "coverage") == 0) {
1755            array = element->pn_right;            array = element->pn_right;
1756            if (array->pn_type != TOK_RB) {            if (array->pn_type != TOK_RB) {
1757              return -1;              result = -1;
1758                goto done;
1759            }            }
1760          }          }
1761          else if (strcmp(s, "source") == 0) {          else if (strcmp(s, "source") == 0) {
1762            source = element->pn_right;            source = element->pn_right;
1763            if (source->pn_type != TOK_RB) {            if (source->pn_type != TOK_RB) {
1764              return -1;              result = -1;
1765                goto done;
1766            }            }
1767          }          }
1768          else {          else {
1769            return -1;            result = -1;
1770              goto done;
1771          }          }
1772        }        }
1773      }      }
1774      else {      else {
1775        return -1;        result = -1;
1776          goto done;
1777      }      }
1778    
1779      if (array == NULL) {      if (array == NULL) {
1780        return -1;        result = -1;
1781          goto done;
1782      }      }
1783    
1784      /* look up the file in the coverage table */      /* look up the file in the coverage table */
# Line 1456  Line 1790 
1790        file_coverage->id = id;        file_coverage->id = id;
1791        file_coverage->num_coverage_lines = array->pn_count;        file_coverage->num_coverage_lines = array->pn_count;
1792        file_coverage->coverage_lines = xnew(int, array->pn_count);        file_coverage->coverage_lines = xnew(int, array->pn_count);
1793        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);  
       }  
1794    
1795        /* set coverage for all lines */        /* set coverage for all lines */
1796        uint32 i = 0;        uint32 i = 0;
# Line 1482  Line 1802 
1802            file_coverage->coverage_lines[i] = -1;            file_coverage->coverage_lines[i] = -1;
1803          }          }
1804          else {          else {
1805            return -1;            result = -1;
1806              goto done;
1807          }          }
1808        }        }
1809        assert(i == array->pn_count);        assert(i == array->pn_count);
# Line 1498  Line 1819 
1819        /* sanity check */        /* sanity check */
1820        assert(strcmp(file_coverage->id, id_bytes) == 0);        assert(strcmp(file_coverage->id, id_bytes) == 0);
1821        if (file_coverage->num_coverage_lines != array->pn_count) {        if (file_coverage->num_coverage_lines != array->pn_count) {
1822          return -2;          result = -2;
1823            goto done;
1824        }        }
1825    
1826        /* merge the coverage */        /* merge the coverage */
# Line 1506  Line 1828 
1828        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++) {
1829          if (element->pn_type == TOK_NUMBER) {          if (element->pn_type == TOK_NUMBER) {
1830            if (file_coverage->coverage_lines[i] == -1) {            if (file_coverage->coverage_lines[i] == -1) {
1831              return -2;              result = -2;
1832                goto done;
1833            }            }
1834            file_coverage->coverage_lines[i] += (int) element->pn_dval;            file_coverage->coverage_lines[i] += (int) element->pn_dval;
1835          }          }
1836          else if (element->pn_type == TOK_PRIMARY && element->pn_op == JSOP_NULL) {          else if (element->pn_type == TOK_PRIMARY && element->pn_op == JSOP_NULL) {
1837            if (file_coverage->coverage_lines[i] != -1) {            if (file_coverage->coverage_lines[i] != -1) {
1838              return -2;              result = -2;
1839                goto done;
1840            }            }
1841          }          }
1842          else {          else {
1843            return -1;            result = -1;
1844              goto done;
1845          }          }
1846        }        }
1847        assert(i == array->pn_count);        assert(i == array->pn_count);
1848        }
1849    
1850        /* if this JSON file has source, use it */      /* if this JSON file has source, use it */
1851        if (file_coverage->source_lines == NULL && source != NULL) {      if (file_coverage->source_lines == NULL && source != NULL) {
1852          file_coverage->num_source_lines = source->pn_count;        file_coverage->num_source_lines = source->pn_count;
1853          file_coverage->source_lines = xnew(char *, source->pn_count);        file_coverage->source_lines = xnew(char *, source->pn_count);
1854          uint32 i = 0;        uint32 i = 0;
1855          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++) {
1856            if (element->pn_type != TOK_STRING) {          if (element->pn_type != TOK_STRING) {
1857              return -1;            result = -1;
1858            }            goto done;
           file_coverage->source_lines[i] = xstrdup(JS_GetStringBytes(ATOM_TO_STRING(element->pn_atom)));  
1859          }          }
1860          assert(i == source->pn_count);          file_coverage->source_lines[i] = xstrdup(JS_GetStringBytes(ATOM_TO_STRING(element->pn_atom)));
1861        }        }
1862          assert(i == source->pn_count);
1863      }      }
1864    }    }
1865    
1866    return 0;  done:
1867      js_FinishParseContext(context, &parse_context);
1868      return result;
1869  }  }

Legend:
Removed from v.240  
changed lines
  Added in v.376

  ViewVC Help
Powered by ViewVC 1.1.24