/[jscoverage]/trunk/instrument-js.c
ViewVC logotype

Diff of /trunk/instrument-js.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 246 by siliconforks, Sun Oct 5 18:05:42 2008 UTC revision 378 by siliconforks, Tue Oct 28 05:31:03 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 146  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 195  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 205  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 240  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 output_destructuring_expression(JSParseNode * node, Stream * f) {
329      switch (node->pn_type) {
330      case TOK_NAME:
331        assert(node->pn_arity == PN_NAME);
332        print_string_atom(node->pn_atom, f);
333        if (node->pn_expr != NULL) {
334          Stream_write_string(f, " = ");
335          instrument_expression(node->pn_expr, f);
336        }
337        break;
338      case TOK_RB:
339        Stream_write_char(f, '[');
340        for (JSParseNode * p = node->pn_head; p != NULL; p = p->pn_next) {
341          if (p != node->pn_head) {
342            Stream_write_string(f, ", ");
343          }
344          /* TOK_COMMA is a special case: a hole in the array */
345          if (p->pn_type != TOK_COMMA) {
346            output_destructuring_expression(p, f);
347          }
348        }
349        if (node->pn_extra == PNX_ENDCOMMA) {
350          Stream_write_char(f, ',');
351        }
352        Stream_write_char(f, ']');
353        break;
354      case TOK_RC:
355        Stream_write_char(f, '{');
356        for (JSParseNode * p = node->pn_head; p != NULL; p = p->pn_next) {
357          if (p != node->pn_head) {
358            Stream_write_string(f, ", ");
359          }
360          if (p->pn_type != TOK_COLON) {
361            fatal_source(file_id, p->pn_pos.begin.lineno, "unsupported node type (%d)", p->pn_type);
362          }
363          instrument_expression(p->pn_left, f);
364          Stream_write_string(f, ": ");
365          output_destructuring_expression(p->pn_right, f);
366        }
367        Stream_write_char(f, '}');
368        break;
369      case TOK_ASSIGN:
370        output_destructuring_expression(node->pn_left, f);
371        Stream_write_string(f, " = ");
372        instrument_expression(node->pn_right, f);
373        break;
374      default:
375        fatal_source(file_id, node->pn_pos.begin.lineno, "unsupported node type (%d)", node->pn_type);
376        break;
377      }
378    }
379    
380  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) {
381      assert(node->pn_type == TOK_FUNCTION);
382    assert(node->pn_arity == PN_FUNC);    assert(node->pn_arity == PN_FUNC);
383    assert(ATOM_IS_OBJECT(node->pn_funAtom));    JSObject * object = node->pn_funpob->object;
   JSObject * object = ATOM_TO_OBJECT(node->pn_funAtom);  
384    assert(JS_ObjectIsFunction(context, object));    assert(JS_ObjectIsFunction(context, object));
385    JSFunction * function = (JSFunction *) JS_GetPrivate(context, object);    JSFunction * function = (JSFunction *) JS_GetPrivate(context, object);
386    assert(function);    assert(function);
387    assert(object == function->object);    assert(object == &function->object);
388    Stream_printf(f, "%*s", indent, "");    Stream_printf(f, "%*s", indent, "");
389    if (type == FUNCTION_NORMAL) {    if (type == FUNCTION_NORMAL) {
390      Stream_write_string(f, "function");      Stream_write_string(f, "function ");
391    }    }
392    
393    /* function name */    /* function name */
394    if (function->atom) {    if (function->atom) {
     Stream_write_char(f, ' ');  
395      print_string_atom(function->atom, f);      print_string_atom(function->atom, f);
396    }    }
397    
398    /* function parameters */    /*
399    Stream_write_string(f, "(");    function parameters - see JS_DecompileFunction in jsapi.cpp, which calls
400    JSAtom ** params = xnew(JSAtom *, function->nargs);    js_DecompileFunction in jsopcode.cpp
401    for (int i = 0; i < function->nargs; i++) {    */
402      /* initialize to NULL for sanity check */    Stream_write_char(f, '(');
403      params[i] = NULL;    JSArenaPool pool;
404    }    JS_INIT_ARENA_POOL(&pool, "instrument_function", 256, 1, &context->scriptStackQuota);
405    JSScope * scope = OBJ_SCOPE(object);    jsuword * local_names = NULL;
406    for (JSScopeProperty * scope_property = SCOPE_LAST_PROP(scope); scope_property != NULL; scope_property = scope_property->parent) {    if (JS_GET_LOCAL_NAME_COUNT(function)) {
407      if (scope_property->getter != js_GetArgument) {      local_names = js_GetLocalNameArray(context, function, &pool);
408        continue;      if (local_names == NULL) {
409          fatal("out of memory");
410      }      }
     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);  
411    }    }
412      bool destructuring = false;
413    for (int i = 0; i < function->nargs; i++) {    for (int i = 0; i < function->nargs; i++) {
     assert(params[i] != NULL);  
414      if (i > 0) {      if (i > 0) {
415        Stream_write_string(f, ", ");        Stream_write_string(f, ", ");
416      }      }
417      if (ATOM_IS_STRING(params[i])) {      JSAtom * param = JS_LOCAL_NAME_TO_ATOM(local_names[i]);
418        print_string_atom(params[i], f);      if (param == NULL) {
419          destructuring = true;
420          JSParseNode * expression = NULL;
421          assert(node->pn_body->pn_type == TOK_LC || node->pn_body->pn_type == TOK_BODY);
422          JSParseNode * semi = node->pn_body->pn_head;
423          assert(semi->pn_type == TOK_SEMI);
424          JSParseNode * comma = semi->pn_kid;
425          assert(comma->pn_type == TOK_COMMA);
426          for (JSParseNode * p = comma->pn_head; p != NULL; p = p->pn_next) {
427            assert(p->pn_type == TOK_ASSIGN);
428            JSParseNode * rhs = p->pn_right;
429            assert(JSSTRING_LENGTH(ATOM_TO_STRING(rhs->pn_atom)) == 0);
430            if (rhs->pn_slot == i) {
431              expression = p->pn_left;
432              break;
433            }
434          }
435          assert(expression != NULL);
436          output_destructuring_expression(expression, f);
437        }
438        else {
439          print_string_atom(param, f);
440      }      }
441    }    }
442      JS_FinishArenaPool(&pool);
443    Stream_write_string(f, ") {\n");    Stream_write_string(f, ") {\n");
   free(params);  
444    
445    /* function body */    /* function body */
446    instrument_statement(node->pn_body, f, indent + 2, false);    if (function->flags & JSFUN_EXPR_CLOSURE) {
447        /* expression closure - use output_statement instead of instrument_statement */
448        if (node->pn_body->pn_type == TOK_BODY) {
449          assert(node->pn_body->pn_arity == PN_LIST);
450          assert(node->pn_body->pn_count == 2);
451          output_statement(node->pn_body->pn_head->pn_next, f, indent + 2, false);
452        }
453        else {
454          output_statement(node->pn_body, f, indent + 2, false);
455        }
456      }
457      else {
458        assert(node->pn_body->pn_type == TOK_LC);
459        assert(node->pn_body->pn_arity == PN_LIST);
460        JSParseNode * p = node->pn_body->pn_head;
461        if (destructuring) {
462          p = p->pn_next;
463        }
464        for (; p != NULL; p = p->pn_next) {
465          instrument_statement(p, f, indent + 2, false);
466        }
467      }
468    
469    Stream_write_string(f, "}\n");    Stream_write_string(f, "}\n");
470  }  }
471    
472  static void instrument_function_call(JSParseNode * node, Stream * f) {  static void instrument_function_call(JSParseNode * node, Stream * f) {
473    instrument_expression(node->pn_head, f);    JSParseNode * function_node = node->pn_head;
474      if (function_node->pn_type == TOK_FUNCTION) {
475        JSObject * object = function_node->pn_funpob->object;
476        assert(JS_ObjectIsFunction(context, object));
477        JSFunction * function = (JSFunction *) JS_GetPrivate(context, object);
478        assert(function);
479        assert(object == &function->object);
480    
481        if (function_node->pn_flags & TCF_GENEXP_LAMBDA) {
482          /* it's a generator expression */
483          Stream_write_char(f, '(');
484          output_array_comprehension_or_generator_expression(function_node->pn_body, f);
485          Stream_write_char(f, ')');
486          return;
487        }
488        else {
489          Stream_write_char(f, '(');
490          instrument_expression(function_node, f);
491          Stream_write_char(f, ')');
492        }
493      }
494      else {
495        instrument_expression(function_node, f);
496      }
497    Stream_write_char(f, '(');    Stream_write_char(f, '(');
498    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) {
499      if (p != node->pn_head->pn_next) {      if (p != node->pn_head->pn_next) {
500        Stream_write_string(f, ", ");        Stream_write_string(f, ", ");
501      }      }
# Line 312  Line 504 
504    Stream_write_char(f, ')');    Stream_write_char(f, ')');
505  }  }
506    
507    static void instrument_declarations(JSParseNode * list, Stream * f) {
508      assert(list->pn_arity == PN_LIST);
509      for (JSParseNode * p = list->pn_head; p != NULL; p = p->pn_next) {
510        if (p != list->pn_head) {
511          Stream_write_string(f, ", ");
512        }
513        output_destructuring_expression(p, f);
514      }
515    }
516    
517  /*  /*
518  See <Expressions> in jsparse.h.  See <Expressions> in jsparse.h.
519  TOK_FUNCTION is handled as a statement and as an expression.  TOK_FUNCTION is handled as a statement and as an expression.
# Line 339  Line 541 
541      }      }
542      break;      break;
543    case TOK_ASSIGN:    case TOK_ASSIGN:
544      instrument_expression(node->pn_left, f);      switch (node->pn_left->pn_type) {
545        case TOK_RB:
546          output_destructuring_expression(node->pn_left, f);
547          break;
548        case TOK_RC:
549          Stream_write_char(f, '(');
550          output_destructuring_expression(node->pn_left, f);
551          Stream_write_char(f, ')');
552          break;
553        default:
554          instrument_expression(node->pn_left, f);
555          break;
556        }
557      Stream_write_char(f, ' ');      Stream_write_char(f, ' ');
558      switch (node->pn_op) {      switch (node->pn_op) {
559      case JSOP_ADD:      case JSOP_ADD:
# Line 370  Line 584 
584      instrument_expression(node->pn_kid3, f);      instrument_expression(node->pn_kid3, f);
585      break;      break;
586    case TOK_OR:    case TOK_OR:
     instrument_expression(node->pn_left, f);  
     Stream_write_string(f, " || ");  
     instrument_expression(node->pn_right, f);  
     break;  
587    case TOK_AND:    case TOK_AND:
     instrument_expression(node->pn_left, f);  
     Stream_write_string(f, " && ");  
     instrument_expression(node->pn_right, f);  
     break;  
588    case TOK_BITOR:    case TOK_BITOR:
589    case TOK_BITXOR:    case TOK_BITXOR:
590    case TOK_BITAND:    case TOK_BITAND:
# Line 434  Line 640 
640        instrument_expression(node->pn_kid, f);        instrument_expression(node->pn_kid, f);
641        break;        break;
642      default:      default:
643        abort();        fatal_source(file_id, node->pn_pos.begin.lineno, "unknown operator (%d)", node->pn_op);
644        break;        break;
645      }      }
646      break;      break;
# Line 482  Line 688 
688      instrument_expression(node->pn_kid, f);      instrument_expression(node->pn_kid, f);
689      break;      break;
690    case TOK_DOT:    case TOK_DOT:
691        /* numeric literals must be parenthesized */
692        switch (node->pn_expr->pn_type) {
693        case TOK_NUMBER:
694          Stream_write_char(f, '(');
695          instrument_expression(node->pn_expr, f);
696          Stream_write_char(f, ')');
697          break;
698        default:
699          instrument_expression(node->pn_expr, f);
700          break;
701        }
702      /*      /*
703      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'
704      contains illegal characters, we have to use the subscript syntax instead of      contains illegal characters, we have to use the subscript syntax instead of
705      the dot syntax.      the dot syntax.
706      */      */
     instrument_expression(node->pn_expr, f);  
707      assert(ATOM_IS_STRING(node->pn_atom));      assert(ATOM_IS_STRING(node->pn_atom));
708      {      {
709        JSString * s = ATOM_TO_STRING(node->pn_atom);        JSString * s = ATOM_TO_STRING(node->pn_atom);
710        /* XXX - semantics changed in 1.7 */        bool must_quote;
711        if (! ATOM_KEYWORD(node->pn_atom) && js_IsIdentifier(s)) {        if (JSSTRING_LENGTH(s) == 0) {
712          Stream_write_char(f, '.');          must_quote = true;
713          print_string_atom(node->pn_atom, f);        }
714          else if (js_CheckKeyword(JSSTRING_CHARS(s), JSSTRING_LENGTH(s)) != TOK_EOF) {
715            must_quote = true;
716          }
717          else if (! js_IsIdentifier(s)) {
718            must_quote = true;
719        }        }
720        else {        else {
721            must_quote = false;
722          }
723          if (must_quote) {
724          Stream_write_char(f, '[');          Stream_write_char(f, '[');
725          print_quoted_string_atom(node->pn_atom, f);          print_quoted_string_atom(node->pn_atom, f);
726          Stream_write_char(f, ']');          Stream_write_char(f, ']');
727        }        }
728          else {
729            Stream_write_char(f, '.');
730            print_string_atom(node->pn_atom, f);
731          }
732      }      }
733      break;      break;
734    case TOK_LB:    case TOK_LB:
# Line 529  Line 757 
757      Stream_write_char(f, ']');      Stream_write_char(f, ']');
758      break;      break;
759    case TOK_RC:    case TOK_RC:
760        Stream_write_char(f, '(');
761      Stream_write_char(f, '{');      Stream_write_char(f, '{');
762      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) {
763        assert(p->pn_type == TOK_COLON);        if (p->pn_type != TOK_COLON) {
764            fatal_source(file_id, p->pn_pos.begin.lineno, "unsupported node type (%d)", p->pn_type);
765          }
766        if (p != node->pn_head) {        if (p != node->pn_head) {
767          Stream_write_string(f, ", ");          Stream_write_string(f, ", ");
768        }        }
# Line 539  Line 770 
770        /* check whether this is a getter or setter */        /* check whether this is a getter or setter */
771        switch (p->pn_op) {        switch (p->pn_op) {
772        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;  
773        case JSOP_SETTER:        case JSOP_SETTER:
774          Stream_write_string(f, "set ");          if (p->pn_op == JSOP_GETTER) {
775              Stream_write_string(f, "get ");
776            }
777            else {
778              Stream_write_string(f, "set ");
779            }
780          instrument_expression(p->pn_left, f);          instrument_expression(p->pn_left, f);
781            if (p->pn_right->pn_type != TOK_FUNCTION) {
782              fatal_source(file_id, p->pn_pos.begin.lineno, "expected function");
783            }
784          instrument_function(p->pn_right, f, 0, FUNCTION_GETTER_OR_SETTER);          instrument_function(p->pn_right, f, 0, FUNCTION_GETTER_OR_SETTER);
785          break;          break;
786        default:        default:
# Line 556  Line 791 
791        }        }
792      }      }
793      Stream_write_char(f, '}');      Stream_write_char(f, '}');
794        Stream_write_char(f, ')');
795      break;      break;
796    case TOK_RP:    case TOK_RP:
797      Stream_write_char(f, '(');      Stream_write_char(f, '(');
# Line 568  Line 804 
804    case TOK_STRING:    case TOK_STRING:
805      print_quoted_string_atom(node->pn_atom, f);      print_quoted_string_atom(node->pn_atom, f);
806      break;      break;
807    case TOK_OBJECT:    case TOK_REGEXP:
808      switch (node->pn_op) {      assert(node->pn_op == JSOP_REGEXP);
809      case JSOP_OBJECT:      {
810        /* I assume this is JSOP_REGEXP */        JSObject * object = node->pn_pob->object;
811        abort();        jsval result;
812        break;        js_regexp_toString(context, object, &result);
813      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;  
814      }      }
815      break;      break;
816    case TOK_NUMBER:    case TOK_NUMBER:
# Line 631  Line 856 
856      Stream_write_string(f, " in ");      Stream_write_string(f, " in ");
857      instrument_expression(node->pn_right, f);      instrument_expression(node->pn_right, f);
858      break;      break;
859    default:    case TOK_LEXICALSCOPE:
860      fatal("unsupported node type in file %s: %d", file_id, node->pn_type);      assert(node->pn_arity == PN_NAME);
861    }      assert(node->pn_expr->pn_type == TOK_LET);
862  }      assert(node->pn_expr->pn_arity == PN_BINARY);
863        Stream_write_string(f, "let(");
864  static void instrument_var_statement(JSParseNode * node, Stream * f, int indent) {      assert(node->pn_expr->pn_left->pn_type == TOK_LP);
865    assert(node->pn_arity == PN_LIST);      assert(node->pn_expr->pn_left->pn_arity == PN_LIST);
866    Stream_printf(f, "%*s", indent, "");      instrument_declarations(node->pn_expr->pn_left, f);
867    Stream_write_string(f, "var ");      Stream_write_string(f, ") ");
868    for (struct JSParseNode * p = node->pn_u.list.head; p != NULL; p = p->pn_next) {      instrument_expression(node->pn_expr->pn_right, f);
869      assert(p->pn_type == TOK_NAME);      break;
870      assert(p->pn_arity == PN_NAME);    case TOK_YIELD:
871      if (p != node->pn_head) {      assert(node->pn_arity == PN_UNARY);
872        Stream_write_string(f, ", ");      Stream_write_string(f, "yield");
873        if (node->pn_kid != NULL) {
874          Stream_write_char(f, ' ');
875          instrument_expression(node->pn_kid, f);
876      }      }
877      print_string_atom(p->pn_atom, f);      break;
878      if (p->pn_expr != NULL) {    case TOK_ARRAYCOMP:
879        Stream_write_string(f, " = ");      assert(node->pn_arity == PN_LIST);
880        instrument_expression(p->pn_expr, f);      {
881          JSParseNode * block_node;
882          switch (node->pn_count) {
883          case 1:
884            block_node = node->pn_head;
885            break;
886          case 2:
887            block_node = node->pn_head->pn_next;
888            break;
889          default:
890            abort();
891            break;
892          }
893          Stream_write_char(f, '[');
894          output_array_comprehension_or_generator_expression(block_node, f);
895          Stream_write_char(f, ']');
896      }      }
897        break;
898      case TOK_VAR:
899        assert(node->pn_arity == PN_LIST);
900        Stream_write_string(f, "var ");
901        instrument_declarations(node, f);
902        break;
903      case TOK_LET:
904        assert(node->pn_arity == PN_LIST);
905        Stream_write_string(f, "let ");
906        instrument_declarations(node, f);
907        break;
908      default:
909        fatal_source(file_id, node->pn_pos.begin.lineno, "unsupported node type (%d)", node->pn_type);
910    }    }
911  }  }
912    
# Line 679  Line 935 
935      uint16_t line = node->pn_pos.begin.lineno;      uint16_t line = node->pn_pos.begin.lineno;
936      if (! is_jscoverage_if) {      if (! is_jscoverage_if) {
937        if (line > num_lines) {        if (line > num_lines) {
938          fatal("%s: script contains more than 65,535 lines", file_id);          fatal("file %s contains more than 65,535 lines", file_id);
939        }        }
940        if (line >= 2 && exclusive_directives[line - 2]) {        if (line >= 2 && exclusive_directives[line - 2]) {
941          is_jscoverage_if = true;          is_jscoverage_if = true;
# Line 727  Line 983 
983      Stream_write_string(f, "switch (");      Stream_write_string(f, "switch (");
984      instrument_expression(node->pn_left, f);      instrument_expression(node->pn_left, f);
985      Stream_write_string(f, ") {\n");      Stream_write_string(f, ") {\n");
986      for (struct JSParseNode * p = node->pn_right->pn_head; p != NULL; p = p->pn_next) {      {
987        Stream_printf(f, "%*s", indent, "");        JSParseNode * list = node->pn_right;
988        switch (p->pn_type) {        if (list->pn_type == TOK_LEXICALSCOPE) {
989        case TOK_CASE:          list = list->pn_expr;
990          Stream_write_string(f, "case ");        }
991          instrument_expression(p->pn_left, f);        for (struct JSParseNode * p = list->pn_head; p != NULL; p = p->pn_next) {
992          Stream_write_string(f, ":\n");          Stream_printf(f, "%*s", indent, "");
993          break;          switch (p->pn_type) {
994        case TOK_DEFAULT:          case TOK_CASE:
995          Stream_write_string(f, "default:\n");            Stream_write_string(f, "case ");
996          break;            instrument_expression(p->pn_left, f);
997        default:            Stream_write_string(f, ":\n");
998          abort();            break;
999          break;          case TOK_DEFAULT:
1000              Stream_write_string(f, "default:\n");
1001              break;
1002            default:
1003              abort();
1004              break;
1005            }
1006            instrument_statement(p->pn_right, f, indent + 2, false);
1007        }        }
       instrument_statement(p->pn_right, f, indent + 2, false);  
1008      }      }
1009      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
1010      Stream_write_string(f, "}\n");      Stream_write_string(f, "}\n");
# Line 774  Line 1036 
1036    case TOK_FOR:    case TOK_FOR:
1037      assert(node->pn_arity == PN_BINARY);      assert(node->pn_arity == PN_BINARY);
1038      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
     Stream_write_string(f, "for (");  
1039      switch (node->pn_left->pn_type) {      switch (node->pn_left->pn_type) {
1040      case TOK_IN:      case TOK_IN:
1041        /* for/in */        /* for/in */
1042        assert(node->pn_left->pn_arity == PN_BINARY);        assert(node->pn_left->pn_arity == PN_BINARY);
1043        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);  
1044        break;        break;
1045      case TOK_RESERVED:      case TOK_RESERVED:
1046        /* for (;;) */        /* for (;;) */
1047        assert(node->pn_left->pn_arity == PN_TERNARY);        assert(node->pn_left->pn_arity == PN_TERNARY);
1048          Stream_write_string(f, "for (");
1049        if (node->pn_left->pn_kid1) {        if (node->pn_left->pn_kid1) {
1050          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);  
         }  
1051        }        }
1052        Stream_write_string(f, ";");        Stream_write_string(f, ";");
1053        if (node->pn_left->pn_kid2) {        if (node->pn_left->pn_kid2) {
# Line 821  Line 1059 
1059          Stream_write_char(f, ' ');          Stream_write_char(f, ' ');
1060          instrument_expression(node->pn_left->pn_kid3, f);          instrument_expression(node->pn_left->pn_kid3, f);
1061        }        }
1062          Stream_write_char(f, ')');
1063        break;        break;
1064      default:      default:
1065        abort();        abort();
1066        break;        break;
1067      }      }
1068      Stream_write_string(f, ") {\n");      Stream_write_string(f, " {\n");
1069      instrument_statement(node->pn_right, f, indent + 2, false);      instrument_statement(node->pn_right, f, indent + 2, false);
1070      Stream_write_string(f, "}\n");      Stream_write_string(f, "}\n");
1071      break;      break;
# Line 843  Line 1082 
1082      instrument_statement(node->pn_kid1, f, indent + 2, false);      instrument_statement(node->pn_kid1, f, indent + 2, false);
1083      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
1084      Stream_write_string(f, "}\n");      Stream_write_string(f, "}\n");
1085      {      if (node->pn_kid2) {
1086        for (JSParseNode * catch = node->pn_kid2; catch != NULL; catch = catch->pn_kid2) {        assert(node->pn_kid2->pn_type == TOK_RESERVED);
1087          for (JSParseNode * scope = node->pn_kid2->pn_head; scope != NULL; scope = scope->pn_next) {
1088            assert(scope->pn_type == TOK_LEXICALSCOPE);
1089            JSParseNode * catch = scope->pn_expr;
1090          assert(catch->pn_type == TOK_CATCH);          assert(catch->pn_type == TOK_CATCH);
1091          Stream_printf(f, "%*s", indent, "");          Stream_printf(f, "%*s", indent, "");
1092          Stream_write_string(f, "catch (");          Stream_write_string(f, "catch (");
1093          assert(catch->pn_kid1->pn_arity == PN_NAME);          output_destructuring_expression(catch->pn_kid1, f);
1094          print_string_atom(catch->pn_kid1->pn_atom, f);          if (catch->pn_kid2) {
         if (catch->pn_kid1->pn_expr) {  
1095            Stream_write_string(f, " if ");            Stream_write_string(f, " if ");
1096            instrument_expression(catch->pn_kid1->pn_expr, f);            instrument_expression(catch->pn_kid2, f);
1097          }          }
1098          Stream_write_string(f, ") {\n");          Stream_write_string(f, ") {\n");
1099          instrument_statement(catch->pn_kid3, f, indent + 2, false);          instrument_statement(catch->pn_kid3, f, indent + 2, false);
# Line 894  Line 1135 
1135      Stream_write_string(f, "}\n");      Stream_write_string(f, "}\n");
1136      break;      break;
1137    case TOK_VAR:    case TOK_VAR:
1138      instrument_var_statement(node, f, indent);      Stream_printf(f, "%*s", indent, "");
1139        instrument_expression(node, f);
1140      Stream_write_string(f, ";\n");      Stream_write_string(f, ";\n");
1141      break;      break;
1142    case TOK_RETURN:    case TOK_RETURN:
# Line 916  Line 1158 
1158      Stream_write_string(f, ";\n");      Stream_write_string(f, ";\n");
1159      break;      break;
1160    case TOK_COLON:    case TOK_COLON:
1161      {
1162      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 ...  
     */  
1163      Stream_printf(f, "%*s", indent < 2? 0: indent - 2, "");      Stream_printf(f, "%*s", indent < 2? 0: indent - 2, "");
1164      print_string_atom(node->pn_atom, f);      print_string_atom(node->pn_atom, f);
1165      Stream_write_string(f, ":\n");      Stream_write_string(f, ":\n");
1166      /*      JSParseNode * labelled = node->pn_expr;
1167      ... use output_statement instead of instrument_statement.      if (labelled->pn_type == TOK_LEXICALSCOPE) {
1168      */        labelled = labelled->pn_expr;
1169      output_statement(node->pn_expr, f, indent, false);      }
1170        if (labelled->pn_type == TOK_LC) {
1171          /* labelled block */
1172          Stream_printf(f, "%*s", indent, "");
1173          Stream_write_string(f, "{\n");
1174          instrument_statement(labelled, f, indent + 2, false);
1175          Stream_printf(f, "%*s", indent, "");
1176          Stream_write_string(f, "}\n");
1177        }
1178        else {
1179          /*
1180          This one is tricky: can't output instrumentation between the label and the
1181          statement it's supposed to label, so use output_statement instead of
1182          instrument_statement.
1183          */
1184          output_statement(labelled, f, indent, false);
1185        }
1186        break;
1187      }
1188      case TOK_LEXICALSCOPE:
1189        /* let statement */
1190        assert(node->pn_arity == PN_NAME);
1191        switch (node->pn_expr->pn_type) {
1192        case TOK_LET:
1193          /* let statement */
1194          assert(node->pn_expr->pn_arity == PN_BINARY);
1195          instrument_statement(node->pn_expr, f, indent, false);
1196          break;
1197        case TOK_LC:
1198          /* block */
1199          Stream_printf(f, "%*s", indent, "");
1200          Stream_write_string(f, "{\n");
1201          instrument_statement(node->pn_expr, f, indent + 2, false);
1202          Stream_printf(f, "%*s", indent, "");
1203          Stream_write_string(f, "}\n");
1204          break;
1205        case TOK_FOR:
1206          instrument_statement(node->pn_expr, f, indent, false);
1207          break;
1208        default:
1209          abort();
1210          break;
1211        }
1212        break;
1213      case TOK_LET:
1214        switch (node->pn_arity) {
1215        case PN_BINARY:
1216          /* let statement */
1217          Stream_printf(f, "%*s", indent, "");
1218          Stream_write_string(f, "let (");
1219          assert(node->pn_left->pn_type == TOK_LP);
1220          assert(node->pn_left->pn_arity == PN_LIST);
1221          instrument_declarations(node->pn_left, f);
1222          Stream_write_string(f, ") {\n");
1223          instrument_statement(node->pn_right, f, indent + 2, false);
1224          Stream_printf(f, "%*s", indent, "");
1225          Stream_write_string(f, "}\n");
1226          break;
1227        case PN_LIST:
1228          /* let definition */
1229          Stream_printf(f, "%*s", indent, "");
1230          instrument_expression(node, f);
1231          Stream_write_string(f, ";\n");
1232          break;
1233        default:
1234          abort();
1235          break;
1236        }
1237        break;
1238      case TOK_DEBUGGER:
1239        Stream_printf(f, "%*s", indent, "");
1240        Stream_write_string(f, "debugger;\n");
1241      break;      break;
1242    default:    default:
1243      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);
1244    }    }
1245  }  }
1246    
# Line 940  Line 1250 
1250  TOK_EXPORT, TOK_IMPORT are not handled.  TOK_EXPORT, TOK_IMPORT are not handled.
1251  */  */
1252  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) {
1253    if (node->pn_type != TOK_LC) {    if (node->pn_type != TOK_LC && node->pn_type != TOK_LEXICALSCOPE) {
1254      uint16_t line = node->pn_pos.begin.lineno;      uint16_t line = node->pn_pos.begin.lineno;
1255      if (line > num_lines) {      if (line > num_lines) {
1256        fatal("%s: script contains more than 65,535 lines", file_id);        fatal("file %s contains more than 65,535 lines", file_id);
1257      }      }
1258    
1259      /* the root node has line number 0 */      /* the root node has line number 0 */
# Line 990  Line 1300 
1300    return true;    return true;
1301  }  }
1302    
1303    static void error_reporter(JSContext * context, const char * message, JSErrorReport * report) {
1304      warn_source(file_id, report->lineno, "%s", message);
1305    }
1306    
1307  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) {
1308    file_id = id;    file_id = id;
1309    
   /* 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);  
   }  
   
1310    /* parse the javascript */    /* parse the javascript */
1311    JSParseNode * node = js_ParseTokenStream(context, global, token_stream);    JSParseContext parse_context;
1312      if (! js_InitParseContext(context, &parse_context, NULL, NULL, characters, num_characters, NULL, NULL, 1)) {
1313        fatal("cannot create token stream from file %s", file_id);
1314      }
1315      JSErrorReporter old_error_reporter = JS_SetErrorReporter(context, error_reporter);
1316      JSParseNode * node = js_ParseScript(context, global, &parse_context);
1317    if (node == NULL) {    if (node == NULL) {
1318      fatal("parse error in file: %s", file_id);      js_ReportUncaughtException(context);
1319        fatal("parse error in file %s", file_id);
1320    }    }
1321      JS_SetErrorReporter(context, old_error_reporter);
1322    num_lines = node->pn_pos.end.lineno;    num_lines = node->pn_pos.end.lineno;
1323    lines = xmalloc(num_lines);    lines = xmalloc(num_lines);
1324    for (unsigned int i = 0; i < num_lines; i++) {    for (unsigned int i = 0; i < num_lines; i++) {
# Line 1022  Line 1337 
1337    size_t i = 0;    size_t i = 0;
1338    while (i < num_characters) {    while (i < num_characters) {
1339      if (line_number == UINT16_MAX) {      if (line_number == UINT16_MAX) {
1340        fatal("%s: script has more than 65,535 lines", file_id);        fatal("file %s contains more than 65,535 lines", file_id);
1341      }      }
1342      line_number++;      line_number++;
1343      size_t line_start = i;      size_t line_start = i;
# Line 1085  Line 1400 
1400    
1401    Stream * instrumented = Stream_new(0);    Stream * instrumented = Stream_new(0);
1402    instrument_statement(node, instrumented, 0, false);    instrument_statement(node, instrumented, 0, false);
1403      js_FinishParseContext(context, &parse_context);
1404    
1405    /* write line number info to the output */    /* write line number info to the output */
1406    Stream_write_string(output, "/* automatically generated by JSCoverage - do not edit */\n");    Stream_write_string(output, "/* automatically generated by JSCoverage - do not edit */\n");
1407    Stream_write_string(output, "if (! top._$jscoverage) {\n  top._$jscoverage = {};\n}\n");    if (jscoverage_mozilla) {
1408    Stream_write_string(output, "var _$jscoverage = top._$jscoverage;\n");      Stream_write_string(output, "try {\n");
1409        Stream_write_string(output, "  Components.utils.import('resource://gre/modules/jscoverage.jsm');\n");
1410        Stream_printf(output, "  dump('%s: successfully imported jscoverage module\\n');\n", id);
1411        Stream_write_string(output, "}\n");
1412        Stream_write_string(output, "catch (e) {\n");
1413        Stream_write_string(output, "  _$jscoverage = {};\n");
1414        Stream_printf(output, "  dump('%s: failed to import jscoverage module - coverage not available for this file\\n');\n", id);
1415        Stream_write_string(output, "}\n");
1416      }
1417      else {
1418        Stream_write_string(output, "if (! top._$jscoverage) {\n  top._$jscoverage = {};\n}\n");
1419        Stream_write_string(output, "var _$jscoverage = top._$jscoverage;\n");
1420      }
1421    Stream_printf(output, "if (! _$jscoverage['%s']) {\n", file_id);    Stream_printf(output, "if (! _$jscoverage['%s']) {\n", file_id);
1422    Stream_printf(output, "  _$jscoverage['%s'] = [];\n", file_id);    Stream_printf(output, "  _$jscoverage['%s'] = [];\n", file_id);
1423    for (int i = 0; i < num_lines; i++) {    for (int i = 0; i < num_lines; i++) {
# Line 1165  Line 1493 
1493            /* line feed (new line) */            /* line feed (new line) */
1494            done = true;            done = true;
1495            break;            break;
1496            /* IE doesn't support this */
1497            /*
1498          case 0xb:          case 0xb:
           /* vertical tab */  
1499            Stream_write_string(output, "\\v");            Stream_write_string(output, "\\v");
1500            break;            break;
1501            */
1502          case 0xc:          case 0xc:
1503            /* form feed */            /* form feed */
1504            Stream_write_string(output, "\\f");            Stream_write_string(output, "\\f");
# Line 1223  Line 1553 
1553            /* line feed (new line) */            /* line feed (new line) */
1554            done = true;            done = true;
1555            break;            break;
1556            /* IE doesn't support this */
1557            /*
1558          case 0xb:          case 0xb:
           /* vertical tab */  
1559            Stream_write_string(output, "\\v");            Stream_write_string(output, "\\v");
1560            break;            break;
1561            */
1562          case 0xc:          case 0xc:
1563            /* form feed */            /* form feed */
1564            Stream_write_string(output, "\\f");            Stream_write_string(output, "\\f");
# Line 1353  Line 1685 
1685  }  }
1686    
1687  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) {
1688      int result = 0;
1689    
1690    jschar * base = js_InflateString(context, (char *) json, &length);    jschar * base = js_InflateString(context, (char *) json, &length);
1691    if (base == NULL) {    if (base == NULL) {
1692      fatal("out of memory");      fatal("out of memory");
# Line 1365  Line 1699 
1699    
1700    JS_free(context, base);    JS_free(context, base);
1701    
1702    JSTokenStream * token_stream = js_NewTokenStream(context, parenthesized_json, length + 2, NULL, 1, NULL);    JSParseContext parse_context;
1703    if (token_stream == NULL) {    if (! js_InitParseContext(context, &parse_context, NULL, NULL, parenthesized_json, length + 2, NULL, NULL, 1)) {
1704      fatal("cannot create token stream");      free(parenthesized_json);
1705        return -1;
1706    }    }
1707      JSParseNode * root = js_ParseScript(context, global, &parse_context);
   JSParseNode * root = js_ParseTokenStream(context, global, token_stream);  
1708    free(parenthesized_json);    free(parenthesized_json);
1709    if (root == NULL) {    if (root == NULL) {
1710      return -1;      result = -1;
1711        goto done;
1712    }    }
1713    
1714    /* root node must be TOK_LC */    /* root node must be TOK_LC */
1715    if (root->pn_type != TOK_LC) {    if (root->pn_type != TOK_LC) {
1716      return -1;      result = -1;
1717        goto done;
1718    }    }
1719    JSParseNode * semi = root->pn_u.list.head;    JSParseNode * semi = root->pn_u.list.head;
1720    
1721    /* 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 */
1722    if (semi->pn_type != TOK_SEMI || semi->pn_next != NULL) {    if (semi->pn_type != TOK_SEMI || semi->pn_next != NULL) {
1723      return -1;      result = -1;
1724        goto done;
1725    }    }
1726    JSParseNode * parenthesized = semi->pn_kid;    JSParseNode * parenthesized = semi->pn_kid;
1727    
1728    /* this must be a parenthesized expression */    /* this must be a parenthesized expression */
1729    if (parenthesized->pn_type != TOK_RP) {    if (parenthesized->pn_type != TOK_RP) {
1730      return -1;      result = -1;
1731        goto done;
1732    }    }
1733    JSParseNode * object = parenthesized->pn_kid;    JSParseNode * object = parenthesized->pn_kid;
1734    
1735    /* this must be an object literal */    /* this must be an object literal */
1736    if (object->pn_type != TOK_RC) {    if (object->pn_type != TOK_RC) {
1737      return -1;      result = -1;
1738        goto done;
1739    }    }
1740    
1741    for (JSParseNode * p = object->pn_head; p != NULL; p = p->pn_next) {    for (JSParseNode * p = object->pn_head; p != NULL; p = p->pn_next) {
1742      /* every element of this list must be TOK_COLON */      /* every element of this list must be TOK_COLON */
1743      if (p->pn_type != TOK_COLON) {      if (p->pn_type != TOK_COLON) {
1744        return -1;        result = -1;
1745          goto done;
1746      }      }
1747    
1748      /* the key must be a string representing the file */      /* the key must be a string representing the file */
1749      JSParseNode * key = p->pn_left;      JSParseNode * key = p->pn_left;
1750      if (key->pn_type != TOK_STRING || ! ATOM_IS_STRING(key->pn_atom)) {      if (key->pn_type != TOK_STRING || ! ATOM_IS_STRING(key->pn_atom)) {
1751        return -1;        result = -1;
1752          goto done;
1753      }      }
1754      char * id_bytes = JS_GetStringBytes(ATOM_TO_STRING(key->pn_atom));      char * id_bytes = JS_GetStringBytes(ATOM_TO_STRING(key->pn_atom));
1755    
1756      /* the value must be an object literal OR an array */      /* the value must be an object literal OR an array */
1757      JSParseNode * value = p->pn_right;      JSParseNode * value = p->pn_right;
1758      if (! (value->pn_type == TOK_RC || value->pn_type == TOK_RB)) {      if (! (value->pn_type == TOK_RC || value->pn_type == TOK_RB)) {
1759        return -1;        result = -1;
1760          goto done;
1761      }      }
1762    
1763      JSParseNode * array = NULL;      JSParseNode * array = NULL;
# Line 1427  Line 1769 
1769      else if (value->pn_type == TOK_RC) {      else if (value->pn_type == TOK_RC) {
1770        /* an object literal */        /* an object literal */
1771        if (value->pn_count != 2) {        if (value->pn_count != 2) {
1772          return -1;          result = -1;
1773            goto done;
1774        }        }
1775        for (JSParseNode * element = value->pn_head; element != NULL; element = element->pn_next) {        for (JSParseNode * element = value->pn_head; element != NULL; element = element->pn_next) {
1776          if (element->pn_type != TOK_COLON) {          if (element->pn_type != TOK_COLON) {
1777            return -1;            result = -1;
1778              goto done;
1779          }          }
1780          JSParseNode * left = element->pn_left;          JSParseNode * left = element->pn_left;
1781          if (left->pn_type != TOK_STRING || ! ATOM_IS_STRING(left->pn_atom)) {          if (left->pn_type != TOK_STRING || ! ATOM_IS_STRING(left->pn_atom)) {
1782            return -1;            result = -1;
1783              goto done;
1784          }          }
1785          const char * s = JS_GetStringBytes(ATOM_TO_STRING(left->pn_atom));          const char * s = JS_GetStringBytes(ATOM_TO_STRING(left->pn_atom));
1786          if (strcmp(s, "coverage") == 0) {          if (strcmp(s, "coverage") == 0) {
1787            array = element->pn_right;            array = element->pn_right;
1788            if (array->pn_type != TOK_RB) {            if (array->pn_type != TOK_RB) {
1789              return -1;              result = -1;
1790                goto done;
1791            }            }
1792          }          }
1793          else if (strcmp(s, "source") == 0) {          else if (strcmp(s, "source") == 0) {
1794            source = element->pn_right;            source = element->pn_right;
1795            if (source->pn_type != TOK_RB) {            if (source->pn_type != TOK_RB) {
1796              return -1;              result = -1;
1797                goto done;
1798            }            }
1799          }          }
1800          else {          else {
1801            return -1;            result = -1;
1802              goto done;
1803          }          }
1804        }        }
1805      }      }
1806      else {      else {
1807        return -1;        result = -1;
1808          goto done;
1809      }      }
1810    
1811      if (array == NULL) {      if (array == NULL) {
1812        return -1;        result = -1;
1813          goto done;
1814      }      }
1815    
1816      /* look up the file in the coverage table */      /* look up the file in the coverage table */
# Line 1484  Line 1834 
1834            file_coverage->coverage_lines[i] = -1;            file_coverage->coverage_lines[i] = -1;
1835          }          }
1836          else {          else {
1837            return -1;            result = -1;
1838              goto done;
1839          }          }
1840        }        }
1841        assert(i == array->pn_count);        assert(i == array->pn_count);
# Line 1500  Line 1851 
1851        /* sanity check */        /* sanity check */
1852        assert(strcmp(file_coverage->id, id_bytes) == 0);        assert(strcmp(file_coverage->id, id_bytes) == 0);
1853        if (file_coverage->num_coverage_lines != array->pn_count) {        if (file_coverage->num_coverage_lines != array->pn_count) {
1854          return -2;          result = -2;
1855            goto done;
1856        }        }
1857    
1858        /* merge the coverage */        /* merge the coverage */
# Line 1508  Line 1860 
1860        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++) {
1861          if (element->pn_type == TOK_NUMBER) {          if (element->pn_type == TOK_NUMBER) {
1862            if (file_coverage->coverage_lines[i] == -1) {            if (file_coverage->coverage_lines[i] == -1) {
1863              return -2;              result = -2;
1864                goto done;
1865            }            }
1866            file_coverage->coverage_lines[i] += (int) element->pn_dval;            file_coverage->coverage_lines[i] += (int) element->pn_dval;
1867          }          }
1868          else if (element->pn_type == TOK_PRIMARY && element->pn_op == JSOP_NULL) {          else if (element->pn_type == TOK_PRIMARY && element->pn_op == JSOP_NULL) {
1869            if (file_coverage->coverage_lines[i] != -1) {            if (file_coverage->coverage_lines[i] != -1) {
1870              return -2;              result = -2;
1871                goto done;
1872            }            }
1873          }          }
1874          else {          else {
1875            return -1;            result = -1;
1876              goto done;
1877          }          }
1878        }        }
1879        assert(i == array->pn_count);        assert(i == array->pn_count);
# Line 1531  Line 1886 
1886        uint32 i = 0;        uint32 i = 0;
1887        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++) {
1888          if (element->pn_type != TOK_STRING) {          if (element->pn_type != TOK_STRING) {
1889            return -1;            result = -1;
1890              goto done;
1891          }          }
1892          file_coverage->source_lines[i] = xstrdup(JS_GetStringBytes(ATOM_TO_STRING(element->pn_atom)));          file_coverage->source_lines[i] = xstrdup(JS_GetStringBytes(ATOM_TO_STRING(element->pn_atom)));
1893        }        }
# Line 1539  Line 1895 
1895      }      }
1896    }    }
1897    
1898    return 0;  done:
1899      js_FinishParseContext(context, &parse_context);
1900      return result;
1901  }  }

Legend:
Removed from v.246  
changed lines
  Added in v.378

  ViewVC Help
Powered by ViewVC 1.1.24