/[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 317 by siliconforks, Tue Oct 14 01:28:46 2008 UTC revision 377 by siliconforks, Tue Oct 28 05:30:43 2008 UTC
# Line 26  Line 26 
26  #include <string.h>  #include <string.h>
27    
28  #include <jsapi.h>  #include <jsapi.h>
29    #include <jsarena.h>
30  #include <jsatom.h>  #include <jsatom.h>
31    #include <jsemit.h>
32  #include <jsexn.h>  #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 49  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 63  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 74  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 199  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 209  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 244  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 316  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 343  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 374  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 438  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 486  Line 655 
655      instrument_expression(node->pn_kid, f);      instrument_expression(node->pn_kid, f);
656      break;      break;
657    case TOK_DOT:    case TOK_DOT:
658        /* numeric literals, object literals must be parenthesized */
659        switch (node->pn_expr->pn_type) {
660        case TOK_NUMBER:
661        case TOK_RC:
662          Stream_write_char(f, '(');
663          instrument_expression(node->pn_expr, f);
664          Stream_write_char(f, ')');
665          break;
666        default:
667          instrument_expression(node->pn_expr, f);
668          break;
669        }
670      /*      /*
671      This may have originally been x['foo-bar'].  Because the string 'foo-bar'      This may have originally been x['foo-bar'].  Because the string 'foo-bar'
672      contains illegal characters, we have to use the subscript syntax instead of      contains illegal characters, we have to use the subscript syntax instead of
673      the dot syntax.      the dot syntax.
674      */      */
     instrument_expression(node->pn_expr, f);  
675      assert(ATOM_IS_STRING(node->pn_atom));      assert(ATOM_IS_STRING(node->pn_atom));
676      {      {
677        JSString * s = ATOM_TO_STRING(node->pn_atom);        JSString * s = ATOM_TO_STRING(node->pn_atom);
678        /* XXX - semantics changed in 1.7 */        bool must_quote;
679        if (! ATOM_KEYWORD(node->pn_atom) && js_IsIdentifier(s)) {        if (JSSTRING_LENGTH(s) == 0) {
680          Stream_write_char(f, '.');          must_quote = true;
681          print_string_atom(node->pn_atom, f);        }
682          else if (js_CheckKeyword(JSSTRING_CHARS(s), JSSTRING_LENGTH(s)) != TOK_EOF) {
683            must_quote = true;
684          }
685          else if (! js_IsIdentifier(s)) {
686            must_quote = true;
687        }        }
688        else {        else {
689            must_quote = false;
690          }
691          if (must_quote) {
692          Stream_write_char(f, '[');          Stream_write_char(f, '[');
693          print_quoted_string_atom(node->pn_atom, f);          print_quoted_string_atom(node->pn_atom, f);
694          Stream_write_char(f, ']');          Stream_write_char(f, ']');
695        }        }
696          else {
697            Stream_write_char(f, '.');
698            print_string_atom(node->pn_atom, f);
699          }
700      }      }
701      break;      break;
702    case TOK_LB:    case TOK_LB:
# Line 535  Line 727 
727    case TOK_RC:    case TOK_RC:
728      Stream_write_char(f, '{');      Stream_write_char(f, '{');
729      for (struct JSParseNode * p = node->pn_head; p != NULL; p = p->pn_next) {      for (struct JSParseNode * p = node->pn_head; p != NULL; p = p->pn_next) {
730        assert(p->pn_type == TOK_COLON);        if (p->pn_type != TOK_COLON) {
731            fatal_source(file_id, p->pn_pos.begin.lineno, "unsupported node type (%d)", p->pn_type);
732          }
733        if (p != node->pn_head) {        if (p != node->pn_head) {
734          Stream_write_string(f, ", ");          Stream_write_string(f, ", ");
735        }        }
# Line 552  Line 746 
746          }          }
747          instrument_expression(p->pn_left, f);          instrument_expression(p->pn_left, f);
748          if (p->pn_right->pn_type != TOK_FUNCTION) {          if (p->pn_right->pn_type != TOK_FUNCTION) {
749            fatal("parse error: expected function");            fatal_source(file_id, p->pn_pos.begin.lineno, "expected function");
750          }          }
751          instrument_function(p->pn_right, f, 0, FUNCTION_GETTER_OR_SETTER);          instrument_function(p->pn_right, f, 0, FUNCTION_GETTER_OR_SETTER);
752          break;          break;
# Line 576  Line 770 
770    case TOK_STRING:    case TOK_STRING:
771      print_quoted_string_atom(node->pn_atom, f);      print_quoted_string_atom(node->pn_atom, f);
772      break;      break;
773    case TOK_OBJECT:    case TOK_REGEXP:
774      switch (node->pn_op) {      assert(node->pn_op == JSOP_REGEXP);
775      case JSOP_OBJECT:      {
776        /* I assume this is JSOP_REGEXP */        JSObject * object = node->pn_pob->object;
777        abort();        jsval result;
778        break;        js_regexp_toString(context, object, &result);
779      case JSOP_REGEXP:        print_regex(result, f);
       assert(ATOM_IS_OBJECT(node->pn_atom));  
       {  
         JSObject * object = ATOM_TO_OBJECT(node->pn_atom);  
         jsval result;  
         js_regexp_toString(context, object, 0, NULL, &result);  
         print_regex(result, f);  
       }  
       break;  
     default:  
       abort();  
       break;  
780      }      }
781      break;      break;
782    case TOK_NUMBER:    case TOK_NUMBER:
# Line 639  Line 822 
822      Stream_write_string(f, " in ");      Stream_write_string(f, " in ");
823      instrument_expression(node->pn_right, f);      instrument_expression(node->pn_right, f);
824      break;      break;
825    default:    case TOK_LEXICALSCOPE:
826      fatal("unsupported node type in file %s: %d", file_id, node->pn_type);      assert(node->pn_arity == PN_NAME);
827    }      assert(node->pn_expr->pn_type == TOK_LET);
828  }      assert(node->pn_expr->pn_arity == PN_BINARY);
829        Stream_write_string(f, "let(");
830  static void instrument_var_statement(JSParseNode * node, Stream * f, int indent) {      assert(node->pn_expr->pn_left->pn_type == TOK_LP);
831    assert(node->pn_arity == PN_LIST);      assert(node->pn_expr->pn_left->pn_arity == PN_LIST);
832    Stream_printf(f, "%*s", indent, "");      instrument_declarations(node->pn_expr->pn_left, f);
833    Stream_write_string(f, "var ");      Stream_write_string(f, ") ");
834    for (struct JSParseNode * p = node->pn_u.list.head; p != NULL; p = p->pn_next) {      instrument_expression(node->pn_expr->pn_right, f);
835      assert(p->pn_type == TOK_NAME);      break;
836      assert(p->pn_arity == PN_NAME);    case TOK_YIELD:
837      if (p != node->pn_head) {      assert(node->pn_arity == PN_UNARY);
838        Stream_write_string(f, ", ");      Stream_write_string(f, "yield");
839        if (node->pn_kid != NULL) {
840          Stream_write_char(f, ' ');
841          instrument_expression(node->pn_kid, f);
842      }      }
843      print_string_atom(p->pn_atom, f);      break;
844      if (p->pn_expr != NULL) {    case TOK_ARRAYCOMP:
845        Stream_write_string(f, " = ");      assert(node->pn_arity == PN_LIST);
846        instrument_expression(p->pn_expr, f);      {
847          JSParseNode * block_node;
848          switch (node->pn_count) {
849          case 1:
850            block_node = node->pn_head;
851            break;
852          case 2:
853            block_node = node->pn_head->pn_next;
854            break;
855          default:
856            abort();
857            break;
858          }
859          Stream_write_char(f, '[');
860          output_array_comprehension_or_generator_expression(block_node, f);
861          Stream_write_char(f, ']');
862      }      }
863        break;
864      case TOK_VAR:
865        assert(node->pn_arity == PN_LIST);
866        Stream_write_string(f, "var ");
867        instrument_declarations(node, f);
868        break;
869      case TOK_LET:
870        assert(node->pn_arity == PN_LIST);
871        Stream_write_string(f, "let ");
872        instrument_declarations(node, f);
873        break;
874      default:
875        fatal_source(file_id, node->pn_pos.begin.lineno, "unsupported node type (%d)", node->pn_type);
876    }    }
877  }  }
878    
# Line 687  Line 901 
901      uint16_t line = node->pn_pos.begin.lineno;      uint16_t line = node->pn_pos.begin.lineno;
902      if (! is_jscoverage_if) {      if (! is_jscoverage_if) {
903        if (line > num_lines) {        if (line > num_lines) {
904          fatal("%s: script contains more than 65,535 lines", file_id);          fatal("file %s contains more than 65,535 lines", file_id);
905        }        }
906        if (line >= 2 && exclusive_directives[line - 2]) {        if (line >= 2 && exclusive_directives[line - 2]) {
907          is_jscoverage_if = true;          is_jscoverage_if = true;
# Line 735  Line 949 
949      Stream_write_string(f, "switch (");      Stream_write_string(f, "switch (");
950      instrument_expression(node->pn_left, f);      instrument_expression(node->pn_left, f);
951      Stream_write_string(f, ") {\n");      Stream_write_string(f, ") {\n");
952      for (struct JSParseNode * p = node->pn_right->pn_head; p != NULL; p = p->pn_next) {      {
953        Stream_printf(f, "%*s", indent, "");        JSParseNode * list = node->pn_right;
954        switch (p->pn_type) {        if (list->pn_type == TOK_LEXICALSCOPE) {
955        case TOK_CASE:          list = list->pn_expr;
956          Stream_write_string(f, "case ");        }
957          instrument_expression(p->pn_left, f);        for (struct JSParseNode * p = list->pn_head; p != NULL; p = p->pn_next) {
958          Stream_write_string(f, ":\n");          Stream_printf(f, "%*s", indent, "");
959          break;          switch (p->pn_type) {
960        case TOK_DEFAULT:          case TOK_CASE:
961          Stream_write_string(f, "default:\n");            Stream_write_string(f, "case ");
962          break;            instrument_expression(p->pn_left, f);
963        default:            Stream_write_string(f, ":\n");
964          abort();            break;
965          break;          case TOK_DEFAULT:
966              Stream_write_string(f, "default:\n");
967              break;
968            default:
969              abort();
970              break;
971            }
972            instrument_statement(p->pn_right, f, indent + 2, false);
973        }        }
       instrument_statement(p->pn_right, f, indent + 2, false);  
974      }      }
975      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
976      Stream_write_string(f, "}\n");      Stream_write_string(f, "}\n");
# Line 782  Line 1002 
1002    case TOK_FOR:    case TOK_FOR:
1003      assert(node->pn_arity == PN_BINARY);      assert(node->pn_arity == PN_BINARY);
1004      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
     Stream_write_string(f, "for (");  
1005      switch (node->pn_left->pn_type) {      switch (node->pn_left->pn_type) {
1006      case TOK_IN:      case TOK_IN:
1007        /* for/in */        /* for/in */
1008        assert(node->pn_left->pn_arity == PN_BINARY);        assert(node->pn_left->pn_arity == PN_BINARY);
1009        switch (node->pn_left->pn_left->pn_type) {        output_for_in(node, f);
       case TOK_VAR:  
         instrument_var_statement(node->pn_left->pn_left, f, 0);  
         break;  
       case TOK_NAME:  
         instrument_expression(node->pn_left->pn_left, f);  
         break;  
       default:  
         /* this is undocumented: for (x.value in y) */  
         instrument_expression(node->pn_left->pn_left, f);  
         break;  
 /*  
       default:  
         fprintf(stderr, "unexpected node type: %d\n", node->pn_left->pn_left->pn_type);  
         abort();  
         break;  
 */  
       }  
       Stream_write_string(f, " in ");  
       instrument_expression(node->pn_left->pn_right, f);  
1010        break;        break;
1011      case TOK_RESERVED:      case TOK_RESERVED:
1012        /* for (;;) */        /* for (;;) */
1013        assert(node->pn_left->pn_arity == PN_TERNARY);        assert(node->pn_left->pn_arity == PN_TERNARY);
1014          Stream_write_string(f, "for (");
1015        if (node->pn_left->pn_kid1) {        if (node->pn_left->pn_kid1) {
1016          if (node->pn_left->pn_kid1->pn_type == TOK_VAR) {          instrument_expression(node->pn_left->pn_kid1, f);
           instrument_var_statement(node->pn_left->pn_kid1, f, 0);  
         }  
         else {  
           instrument_expression(node->pn_left->pn_kid1, f);  
         }  
1017        }        }
1018        Stream_write_string(f, ";");        Stream_write_string(f, ";");
1019        if (node->pn_left->pn_kid2) {        if (node->pn_left->pn_kid2) {
# Line 829  Line 1025 
1025          Stream_write_char(f, ' ');          Stream_write_char(f, ' ');
1026          instrument_expression(node->pn_left->pn_kid3, f);          instrument_expression(node->pn_left->pn_kid3, f);
1027        }        }
1028          Stream_write_char(f, ')');
1029        break;        break;
1030      default:      default:
1031        abort();        abort();
1032        break;        break;
1033      }      }
1034      Stream_write_string(f, ") {\n");      Stream_write_string(f, " {\n");
1035      instrument_statement(node->pn_right, f, indent + 2, false);      instrument_statement(node->pn_right, f, indent + 2, false);
1036      Stream_write_string(f, "}\n");      Stream_write_string(f, "}\n");
1037      break;      break;
# Line 851  Line 1048 
1048      instrument_statement(node->pn_kid1, f, indent + 2, false);      instrument_statement(node->pn_kid1, f, indent + 2, false);
1049      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
1050      Stream_write_string(f, "}\n");      Stream_write_string(f, "}\n");
1051      {      if (node->pn_kid2) {
1052        for (JSParseNode * catch = node->pn_kid2; catch != NULL; catch = catch->pn_kid2) {        assert(node->pn_kid2->pn_type == TOK_RESERVED);
1053          for (JSParseNode * scope = node->pn_kid2->pn_head; scope != NULL; scope = scope->pn_next) {
1054            assert(scope->pn_type == TOK_LEXICALSCOPE);
1055            JSParseNode * catch = scope->pn_expr;
1056          assert(catch->pn_type == TOK_CATCH);          assert(catch->pn_type == TOK_CATCH);
1057          Stream_printf(f, "%*s", indent, "");          Stream_printf(f, "%*s", indent, "");
1058          Stream_write_string(f, "catch (");          Stream_write_string(f, "catch (");
1059            /* this may not be a name - destructuring assignment */
1060            /*
1061          assert(catch->pn_kid1->pn_arity == PN_NAME);          assert(catch->pn_kid1->pn_arity == PN_NAME);
1062          print_string_atom(catch->pn_kid1->pn_atom, f);          print_string_atom(catch->pn_kid1->pn_atom, f);
1063          if (catch->pn_kid1->pn_expr) {          */
1064            instrument_expression(catch->pn_kid1, f);
1065            if (catch->pn_kid2) {
1066            Stream_write_string(f, " if ");            Stream_write_string(f, " if ");
1067            instrument_expression(catch->pn_kid1->pn_expr, f);            instrument_expression(catch->pn_kid2, f);
1068          }          }
1069          Stream_write_string(f, ") {\n");          Stream_write_string(f, ") {\n");
1070          instrument_statement(catch->pn_kid3, f, indent + 2, false);          instrument_statement(catch->pn_kid3, f, indent + 2, false);
# Line 902  Line 1106 
1106      Stream_write_string(f, "}\n");      Stream_write_string(f, "}\n");
1107      break;      break;
1108    case TOK_VAR:    case TOK_VAR:
1109      instrument_var_statement(node, f, indent);      Stream_printf(f, "%*s", indent, "");
1110        instrument_expression(node, f);
1111      Stream_write_string(f, ";\n");      Stream_write_string(f, ";\n");
1112      break;      break;
1113    case TOK_RETURN:    case TOK_RETURN:
# Line 924  Line 1129 
1129      Stream_write_string(f, ";\n");      Stream_write_string(f, ";\n");
1130      break;      break;
1131    case TOK_COLON:    case TOK_COLON:
1132      {
1133      assert(node->pn_arity == PN_NAME);      assert(node->pn_arity == PN_NAME);
     /*  
     This one is tricky: can't output instrumentation between the label and the  
     statement it's supposed to label ...  
     */  
1134      Stream_printf(f, "%*s", indent < 2? 0: indent - 2, "");      Stream_printf(f, "%*s", indent < 2? 0: indent - 2, "");
1135      print_string_atom(node->pn_atom, f);      print_string_atom(node->pn_atom, f);
1136      Stream_write_string(f, ":\n");      Stream_write_string(f, ":\n");
1137      /*      JSParseNode * labelled = node->pn_expr;
1138      ... use output_statement instead of instrument_statement.      if (labelled->pn_type == TOK_LEXICALSCOPE) {
1139      */        labelled = labelled->pn_expr;
1140      output_statement(node->pn_expr, f, indent, false);      }
1141        if (labelled->pn_type == TOK_LC) {
1142          /* labelled block */
1143          Stream_printf(f, "%*s", indent, "");
1144          Stream_write_string(f, "{\n");
1145          instrument_statement(labelled, f, indent + 2, false);
1146          Stream_printf(f, "%*s", indent, "");
1147          Stream_write_string(f, "}\n");
1148        }
1149        else {
1150          /*
1151          This one is tricky: can't output instrumentation between the label and the
1152          statement it's supposed to label, so use output_statement instead of
1153          instrument_statement.
1154          */
1155          output_statement(labelled, f, indent, false);
1156        }
1157        break;
1158      }
1159      case TOK_LEXICALSCOPE:
1160        /* let statement */
1161        assert(node->pn_arity == PN_NAME);
1162        switch (node->pn_expr->pn_type) {
1163        case TOK_LET:
1164          /* let statement */
1165          assert(node->pn_expr->pn_arity == PN_BINARY);
1166          instrument_statement(node->pn_expr, f, indent, false);
1167          break;
1168        case TOK_LC:
1169          /* block */
1170          Stream_printf(f, "%*s", indent, "");
1171          Stream_write_string(f, "{\n");
1172          instrument_statement(node->pn_expr, f, indent + 2, false);
1173          Stream_printf(f, "%*s", indent, "");
1174          Stream_write_string(f, "}\n");
1175          break;
1176        case TOK_FOR:
1177          instrument_statement(node->pn_expr, f, indent, false);
1178          break;
1179        default:
1180          abort();
1181          break;
1182        }
1183        break;
1184      case TOK_LET:
1185        switch (node->pn_arity) {
1186        case PN_BINARY:
1187          /* let statement */
1188          Stream_printf(f, "%*s", indent, "");
1189          Stream_write_string(f, "let (");
1190          assert(node->pn_left->pn_type == TOK_LP);
1191          assert(node->pn_left->pn_arity == PN_LIST);
1192          instrument_declarations(node->pn_left, f);
1193          Stream_write_string(f, ") {\n");
1194          instrument_statement(node->pn_right, f, indent + 2, false);
1195          Stream_printf(f, "%*s", indent, "");
1196          Stream_write_string(f, "}\n");
1197          break;
1198        case PN_LIST:
1199          /* let definition */
1200          Stream_printf(f, "%*s", indent, "");
1201          instrument_expression(node, f);
1202          Stream_write_string(f, ";\n");
1203          break;
1204        default:
1205          abort();
1206          break;
1207        }
1208        break;
1209      case TOK_DEBUGGER:
1210        Stream_printf(f, "%*s", indent, "");
1211        Stream_write_string(f, "debugger;\n");
1212      break;      break;
1213    default:    default:
1214      fatal("unsupported node type in file %s: %d", file_id, node->pn_type);      fatal_source(file_id, node->pn_pos.begin.lineno, "unsupported node type (%d)", node->pn_type);
1215    }    }
1216  }  }
1217    
# Line 948  Line 1221 
1221  TOK_EXPORT, TOK_IMPORT are not handled.  TOK_EXPORT, TOK_IMPORT are not handled.
1222  */  */
1223  static void instrument_statement(JSParseNode * node, Stream * f, int indent, bool is_jscoverage_if) {  static void instrument_statement(JSParseNode * node, Stream * f, int indent, bool is_jscoverage_if) {
1224    if (node->pn_type != TOK_LC) {    if (node->pn_type != TOK_LC && node->pn_type != TOK_LEXICALSCOPE) {
1225      uint16_t line = node->pn_pos.begin.lineno;      uint16_t line = node->pn_pos.begin.lineno;
1226      if (line > num_lines) {      if (line > num_lines) {
1227        fatal("%s: script contains more than 65,535 lines", file_id);        fatal("file %s contains more than 65,535 lines", file_id);
1228      }      }
1229    
1230      /* the root node has line number 0 */      /* the root node has line number 0 */
# Line 999  Line 1272 
1272  }  }
1273    
1274  static void error_reporter(JSContext * context, const char * message, JSErrorReport * report) {  static void error_reporter(JSContext * context, const char * message, JSErrorReport * report) {
1275    fprintf(stderr, "jscoverage: parse error: line %u: %s\n", report->lineno, message);    warn_source(file_id, report->lineno, "%s", message);
1276  }  }
1277    
1278  void jscoverage_instrument_js(const char * id, const uint16_t * characters, size_t num_characters, Stream * output) {  void jscoverage_instrument_js(const char * id, const uint16_t * characters, size_t num_characters, Stream * output) {
1279    file_id = id;    file_id = id;
1280    
   /* 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);  
   }  
   
1281    /* parse the javascript */    /* parse the javascript */
1282      JSParseContext parse_context;
1283      if (! js_InitParseContext(context, &parse_context, NULL, NULL, characters, num_characters, NULL, NULL, 1)) {
1284        fatal("cannot create token stream from file %s", file_id);
1285      }
1286    JSErrorReporter old_error_reporter = JS_SetErrorReporter(context, error_reporter);    JSErrorReporter old_error_reporter = JS_SetErrorReporter(context, error_reporter);
1287    JSParseNode * node = js_ParseTokenStream(context, global, token_stream);    JSParseNode * node = js_ParseScript(context, global, &parse_context);
1288    if (node == NULL) {    if (node == NULL) {
1289      js_ReportUncaughtException(context);      js_ReportUncaughtException(context);
1290      fatal("parse error in file: %s", file_id);      fatal("parse error in file %s", file_id);
1291    }    }
1292    JS_SetErrorReporter(context, old_error_reporter);    JS_SetErrorReporter(context, old_error_reporter);
1293    num_lines = node->pn_pos.end.lineno;    num_lines = node->pn_pos.end.lineno;
# Line 1037  Line 1308 
1308    size_t i = 0;    size_t i = 0;
1309    while (i < num_characters) {    while (i < num_characters) {
1310      if (line_number == UINT16_MAX) {      if (line_number == UINT16_MAX) {
1311        fatal("%s: script has more than 65,535 lines", file_id);        fatal("file %s contains more than 65,535 lines", file_id);
1312      }      }
1313      line_number++;      line_number++;
1314      size_t line_start = i;      size_t line_start = i;
# Line 1100  Line 1371 
1371    
1372    Stream * instrumented = Stream_new(0);    Stream * instrumented = Stream_new(0);
1373    instrument_statement(node, instrumented, 0, false);    instrument_statement(node, instrumented, 0, false);
1374      js_FinishParseContext(context, &parse_context);
1375    
1376    /* write line number info to the output */    /* write line number info to the output */
1377    Stream_write_string(output, "/* automatically generated by JSCoverage - do not edit */\n");    Stream_write_string(output, "/* automatically generated by JSCoverage - do not edit */\n");
1378    Stream_write_string(output, "if (! top._$jscoverage) {\n  top._$jscoverage = {};\n}\n");    if (jscoverage_mozilla) {
1379    Stream_write_string(output, "var _$jscoverage = top._$jscoverage;\n");      Stream_write_string(output, "try {\n");
1380        Stream_write_string(output, "  Components.utils.import('resource://gre/modules/jscoverage.jsm');\n");
1381        Stream_printf(output, "  dump('%s: successfully imported jscoverage module\\n');\n", id);
1382        Stream_write_string(output, "}\n");
1383        Stream_write_string(output, "catch (e) {\n");
1384        Stream_write_string(output, "  _$jscoverage = {};\n");
1385        Stream_printf(output, "  dump('%s: failed to import jscoverage module - coverage not available for this file\\n');\n", id);
1386        Stream_write_string(output, "}\n");
1387      }
1388      else {
1389        Stream_write_string(output, "if (! top._$jscoverage) {\n  top._$jscoverage = {};\n}\n");
1390        Stream_write_string(output, "var _$jscoverage = top._$jscoverage;\n");
1391      }
1392    Stream_printf(output, "if (! _$jscoverage['%s']) {\n", file_id);    Stream_printf(output, "if (! _$jscoverage['%s']) {\n", file_id);
1393    Stream_printf(output, "  _$jscoverage['%s'] = [];\n", file_id);    Stream_printf(output, "  _$jscoverage['%s'] = [];\n", file_id);
1394    for (int i = 0; i < num_lines; i++) {    for (int i = 0; i < num_lines; i++) {
# Line 1372  Line 1656 
1656  }  }
1657    
1658  int jscoverage_parse_json(Coverage * coverage, const uint8_t * json, size_t length) {  int jscoverage_parse_json(Coverage * coverage, const uint8_t * json, size_t length) {
1659      int result = 0;
1660    
1661    jschar * base = js_InflateString(context, (char *) json, &length);    jschar * base = js_InflateString(context, (char *) json, &length);
1662    if (base == NULL) {    if (base == NULL) {
1663      fatal("out of memory");      fatal("out of memory");
# Line 1384  Line 1670 
1670    
1671    JS_free(context, base);    JS_free(context, base);
1672    
1673    JSTokenStream * token_stream = js_NewTokenStream(context, parenthesized_json, length + 2, NULL, 1, NULL);    JSParseContext parse_context;
1674    if (token_stream == NULL) {    if (! js_InitParseContext(context, &parse_context, NULL, NULL, parenthesized_json, length + 2, NULL, NULL, 1)) {
1675      fatal("cannot create token stream");      free(parenthesized_json);
1676        return -1;
1677    }    }
1678      JSParseNode * root = js_ParseScript(context, global, &parse_context);
   JSParseNode * root = js_ParseTokenStream(context, global, token_stream);  
1679    free(parenthesized_json);    free(parenthesized_json);
1680    if (root == NULL) {    if (root == NULL) {
1681      return -1;      result = -1;
1682        goto done;
1683    }    }
1684    
1685    /* root node must be TOK_LC */    /* root node must be TOK_LC */
1686    if (root->pn_type != TOK_LC) {    if (root->pn_type != TOK_LC) {
1687      return -1;      result = -1;
1688        goto done;
1689    }    }
1690    JSParseNode * semi = root->pn_u.list.head;    JSParseNode * semi = root->pn_u.list.head;
1691    
1692    /* the list must be TOK_SEMI and it must contain only one element */    /* the list must be TOK_SEMI and it must contain only one element */
1693    if (semi->pn_type != TOK_SEMI || semi->pn_next != NULL) {    if (semi->pn_type != TOK_SEMI || semi->pn_next != NULL) {
1694      return -1;      result = -1;
1695        goto done;
1696    }    }
1697    JSParseNode * parenthesized = semi->pn_kid;    JSParseNode * parenthesized = semi->pn_kid;
1698    
1699    /* this must be a parenthesized expression */    /* this must be a parenthesized expression */
1700    if (parenthesized->pn_type != TOK_RP) {    if (parenthesized->pn_type != TOK_RP) {
1701      return -1;      result = -1;
1702        goto done;
1703    }    }
1704    JSParseNode * object = parenthesized->pn_kid;    JSParseNode * object = parenthesized->pn_kid;
1705    
1706    /* this must be an object literal */    /* this must be an object literal */
1707    if (object->pn_type != TOK_RC) {    if (object->pn_type != TOK_RC) {
1708      return -1;      result = -1;
1709        goto done;
1710    }    }
1711    
1712    for (JSParseNode * p = object->pn_head; p != NULL; p = p->pn_next) {    for (JSParseNode * p = object->pn_head; p != NULL; p = p->pn_next) {
1713      /* every element of this list must be TOK_COLON */      /* every element of this list must be TOK_COLON */
1714      if (p->pn_type != TOK_COLON) {      if (p->pn_type != TOK_COLON) {
1715        return -1;        result = -1;
1716          goto done;
1717      }      }
1718    
1719      /* the key must be a string representing the file */      /* the key must be a string representing the file */
1720      JSParseNode * key = p->pn_left;      JSParseNode * key = p->pn_left;
1721      if (key->pn_type != TOK_STRING || ! ATOM_IS_STRING(key->pn_atom)) {      if (key->pn_type != TOK_STRING || ! ATOM_IS_STRING(key->pn_atom)) {
1722        return -1;        result = -1;
1723          goto done;
1724      }      }
1725      char * id_bytes = JS_GetStringBytes(ATOM_TO_STRING(key->pn_atom));      char * id_bytes = JS_GetStringBytes(ATOM_TO_STRING(key->pn_atom));
1726    
1727      /* the value must be an object literal OR an array */      /* the value must be an object literal OR an array */
1728      JSParseNode * value = p->pn_right;      JSParseNode * value = p->pn_right;
1729      if (! (value->pn_type == TOK_RC || value->pn_type == TOK_RB)) {      if (! (value->pn_type == TOK_RC || value->pn_type == TOK_RB)) {
1730        return -1;        result = -1;
1731          goto done;
1732      }      }
1733    
1734      JSParseNode * array = NULL;      JSParseNode * array = NULL;
# Line 1446  Line 1740 
1740      else if (value->pn_type == TOK_RC) {      else if (value->pn_type == TOK_RC) {
1741        /* an object literal */        /* an object literal */
1742        if (value->pn_count != 2) {        if (value->pn_count != 2) {
1743          return -1;          result = -1;
1744            goto done;
1745        }        }
1746        for (JSParseNode * element = value->pn_head; element != NULL; element = element->pn_next) {        for (JSParseNode * element = value->pn_head; element != NULL; element = element->pn_next) {
1747          if (element->pn_type != TOK_COLON) {          if (element->pn_type != TOK_COLON) {
1748            return -1;            result = -1;
1749              goto done;
1750          }          }
1751          JSParseNode * left = element->pn_left;          JSParseNode * left = element->pn_left;
1752          if (left->pn_type != TOK_STRING || ! ATOM_IS_STRING(left->pn_atom)) {          if (left->pn_type != TOK_STRING || ! ATOM_IS_STRING(left->pn_atom)) {
1753            return -1;            result = -1;
1754              goto done;
1755          }          }
1756          const char * s = JS_GetStringBytes(ATOM_TO_STRING(left->pn_atom));          const char * s = JS_GetStringBytes(ATOM_TO_STRING(left->pn_atom));
1757          if (strcmp(s, "coverage") == 0) {          if (strcmp(s, "coverage") == 0) {
1758            array = element->pn_right;            array = element->pn_right;
1759            if (array->pn_type != TOK_RB) {            if (array->pn_type != TOK_RB) {
1760              return -1;              result = -1;
1761                goto done;
1762            }            }
1763          }          }
1764          else if (strcmp(s, "source") == 0) {          else if (strcmp(s, "source") == 0) {
1765            source = element->pn_right;            source = element->pn_right;
1766            if (source->pn_type != TOK_RB) {            if (source->pn_type != TOK_RB) {
1767              return -1;              result = -1;
1768                goto done;
1769            }            }
1770          }          }
1771          else {          else {
1772            return -1;            result = -1;
1773              goto done;
1774          }          }
1775        }        }
1776      }      }
1777      else {      else {
1778        return -1;        result = -1;
1779          goto done;
1780      }      }
1781    
1782      if (array == NULL) {      if (array == NULL) {
1783        return -1;        result = -1;
1784          goto done;
1785      }      }
1786    
1787      /* look up the file in the coverage table */      /* look up the file in the coverage table */
# Line 1503  Line 1805 
1805            file_coverage->coverage_lines[i] = -1;            file_coverage->coverage_lines[i] = -1;
1806          }          }
1807          else {          else {
1808            return -1;            result = -1;
1809              goto done;
1810          }          }
1811        }        }
1812        assert(i == array->pn_count);        assert(i == array->pn_count);
# Line 1519  Line 1822 
1822        /* sanity check */        /* sanity check */
1823        assert(strcmp(file_coverage->id, id_bytes) == 0);        assert(strcmp(file_coverage->id, id_bytes) == 0);
1824        if (file_coverage->num_coverage_lines != array->pn_count) {        if (file_coverage->num_coverage_lines != array->pn_count) {
1825          return -2;          result = -2;
1826            goto done;
1827        }        }
1828    
1829        /* merge the coverage */        /* merge the coverage */
# Line 1527  Line 1831 
1831        for (JSParseNode * element = array->pn_head; element != NULL; element = element->pn_next, i++) {        for (JSParseNode * element = array->pn_head; element != NULL; element = element->pn_next, i++) {
1832          if (element->pn_type == TOK_NUMBER) {          if (element->pn_type == TOK_NUMBER) {
1833            if (file_coverage->coverage_lines[i] == -1) {            if (file_coverage->coverage_lines[i] == -1) {
1834              return -2;              result = -2;
1835                goto done;
1836            }            }
1837            file_coverage->coverage_lines[i] += (int) element->pn_dval;            file_coverage->coverage_lines[i] += (int) element->pn_dval;
1838          }          }
1839          else if (element->pn_type == TOK_PRIMARY && element->pn_op == JSOP_NULL) {          else if (element->pn_type == TOK_PRIMARY && element->pn_op == JSOP_NULL) {
1840            if (file_coverage->coverage_lines[i] != -1) {            if (file_coverage->coverage_lines[i] != -1) {
1841              return -2;              result = -2;
1842                goto done;
1843            }            }
1844          }          }
1845          else {          else {
1846            return -1;            result = -1;
1847              goto done;
1848          }          }
1849        }        }
1850        assert(i == array->pn_count);        assert(i == array->pn_count);
# Line 1550  Line 1857 
1857        uint32 i = 0;        uint32 i = 0;
1858        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++) {
1859          if (element->pn_type != TOK_STRING) {          if (element->pn_type != TOK_STRING) {
1860            return -1;            result = -1;
1861              goto done;
1862          }          }
1863          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)));
1864        }        }
# Line 1558  Line 1866 
1866      }      }
1867    }    }
1868    
1869    return 0;  done:
1870      js_FinishParseContext(context, &parse_context);
1871      return result;
1872  }  }

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

  ViewVC Help
Powered by ViewVC 1.1.24