/[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 349 by siliconforks, Fri Oct 24 16:17:19 2008 UTC revision 391 by siliconforks, Thu Oct 30 17:54:02 2008 UTC
# Line 22  Line 22 
22  #include "instrument-js.h"  #include "instrument-js.h"
23    
24  #include <assert.h>  #include <assert.h>
25    #include <math.h>
26  #include <stdlib.h>  #include <stdlib.h>
27  #include <string.h>  #include <string.h>
28    
29  #include <jsapi.h>  #include <jsapi.h>
30  #include <jsarena.h>  #include <jsarena.h>
31  #include <jsatom.h>  #include <jsatom.h>
32    #include <jsemit.h>
33  #include <jsexn.h>  #include <jsexn.h>
34  #include <jsfun.h>  #include <jsfun.h>
35  #include <jsinterp.h>  #include <jsinterp.h>
# Line 51  Line 53 
53    struct IfDirective * next;    struct IfDirective * next;
54  };  };
55    
56    bool jscoverage_mozilla = false;
57    
58  static bool * exclusive_directives = NULL;  static bool * exclusive_directives = NULL;
59    
60  static JSRuntime * runtime = NULL;  static JSRuntime * runtime = NULL;
# Line 67  Line 71 
71  static uint16_t num_lines = 0;  static uint16_t num_lines = 0;
72    
73  void jscoverage_set_js_version(const char * version) {  void jscoverage_set_js_version(const char * version) {
74    js_version = atoi(version);    js_version = JS_StringToVersion(version);
75      if (js_version != JSVERSION_UNKNOWN) {
76        return;
77      }
78    
79      char * end;
80      js_version = (JSVersion) strtol(version, &end, 10);
81      if ((size_t) (end - version) != strlen(version)) {
82        fatal("invalid version: %s", version);
83      }
84  }  }
85    
86  void jscoverage_init(void) {  void jscoverage_init(void) {
# Line 255  Line 268 
268    }    }
269  }  }
270    
271  static void instrument_expression(JSParseNode * node, Stream * f);  static void output_expression(JSParseNode * node, Stream * f, bool parenthesize_object_literals);
272  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);
273  static void output_statement(JSParseNode * node, Stream * f, int indent, bool is_jscoverage_if);  static void output_statement(JSParseNode * node, Stream * f, int indent, bool is_jscoverage_if);
274    
# Line 272  Line 285 
285      Stream_write_string(f, "each ");      Stream_write_string(f, "each ");
286    }    }
287    Stream_write_char(f, '(');    Stream_write_char(f, '(');
288    instrument_expression(node->pn_left, f);    output_expression(node->pn_left, f, false);
289    Stream_write_char(f, ')');    Stream_write_char(f, ')');
290  }  }
291    
292    static void output_array_comprehension_or_generator_expression(JSParseNode * node, Stream * f) {
293      assert(node->pn_type == TOK_LEXICALSCOPE);
294      assert(node->pn_arity == PN_NAME);
295      JSParseNode * for_node = node->pn_expr;
296      assert(for_node->pn_type == TOK_FOR);
297      assert(for_node->pn_arity == PN_BINARY);
298      JSParseNode * p = for_node;
299      while (p->pn_type == TOK_FOR) {
300        p = p->pn_right;
301      }
302      JSParseNode * if_node = NULL;
303      if (p->pn_type == TOK_IF) {
304        if_node = p;
305        assert(if_node->pn_arity == PN_TERNARY);
306        p = if_node->pn_kid2;
307      }
308      assert(p->pn_arity == PN_UNARY);
309      p = p->pn_kid;
310      if (p->pn_type == TOK_YIELD) {
311        /* for generator expressions */
312        p = p->pn_kid;
313      }
314    
315      output_expression(p, f, false);
316      p = for_node;
317      while (p->pn_type == TOK_FOR) {
318        Stream_write_char(f, ' ');
319        output_for_in(p, f);
320        p = p->pn_right;
321      }
322      if (if_node) {
323        Stream_write_string(f, " if (");
324        output_expression(if_node->pn_kid1, f, false);
325        Stream_write_char(f, ')');
326      }
327    }
328    
329  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) {
330    assert(node->pn_type == TOK_FUNCTION);    assert(node->pn_type == TOK_FUNCTION);
331    assert(node->pn_arity == PN_FUNC);    assert(node->pn_arity == PN_FUNC);
# Line 286  Line 336 
336    assert(object == &function->object);    assert(object == &function->object);
337    Stream_printf(f, "%*s", indent, "");    Stream_printf(f, "%*s", indent, "");
338    if (type == FUNCTION_NORMAL) {    if (type == FUNCTION_NORMAL) {
339      Stream_write_string(f, "function");      Stream_write_string(f, "function ");
340    }    }
341    
342    /* function name */    /* function name */
343    if (function->atom) {    if (function->atom) {
     Stream_write_char(f, ' ');  
344      print_string_atom(function->atom, f);      print_string_atom(function->atom, f);
345    }    }
346    
# Line 299  Line 348 
348    function parameters - see JS_DecompileFunction in jsapi.cpp, which calls    function parameters - see JS_DecompileFunction in jsapi.cpp, which calls
349    js_DecompileFunction in jsopcode.cpp    js_DecompileFunction in jsopcode.cpp
350    */    */
351    Stream_write_string(f, "(");    Stream_write_char(f, '(');
352    JSArenaPool pool;    JSArenaPool pool;
353    JS_INIT_ARENA_POOL(&pool, "instrument_function", 256, 1, &context->scriptStackQuota);    JS_INIT_ARENA_POOL(&pool, "instrument_function", 256, 1, &context->scriptStackQuota);
354    jsuword * local_names = NULL;    jsuword * local_names = NULL;
# Line 309  Line 358 
358        fatal("out of memory");        fatal("out of memory");
359      }      }
360    }    }
361      bool destructuring = false;
362    for (int i = 0; i < function->nargs; i++) {    for (int i = 0; i < function->nargs; i++) {
363      if (i > 0) {      if (i > 0) {
364        Stream_write_string(f, ", ");        Stream_write_string(f, ", ");
365      }      }
366      JSAtom * param = JS_LOCAL_NAME_TO_ATOM(local_names[i]);      JSAtom * param = JS_LOCAL_NAME_TO_ATOM(local_names[i]);
367      print_string_atom(param, f);      if (param == NULL) {
368          destructuring = true;
369          JSParseNode * expression = NULL;
370          assert(node->pn_body->pn_type == TOK_LC || node->pn_body->pn_type == TOK_BODY);
371          JSParseNode * semi = node->pn_body->pn_head;
372          assert(semi->pn_type == TOK_SEMI);
373          JSParseNode * comma = semi->pn_kid;
374          assert(comma->pn_type == TOK_COMMA);
375          for (JSParseNode * p = comma->pn_head; p != NULL; p = p->pn_next) {
376            assert(p->pn_type == TOK_ASSIGN);
377            JSParseNode * rhs = p->pn_right;
378            assert(JSSTRING_LENGTH(ATOM_TO_STRING(rhs->pn_atom)) == 0);
379            if (rhs->pn_slot == i) {
380              expression = p->pn_left;
381              break;
382            }
383          }
384          assert(expression != NULL);
385          output_expression(expression, f, false);
386        }
387        else {
388          print_string_atom(param, f);
389        }
390    }    }
391    JS_FinishArenaPool(&pool);    JS_FinishArenaPool(&pool);
392    Stream_write_string(f, ") {\n");    Stream_write_string(f, ") {\n");
393    
394    /* function body */    /* function body */
395    if (function->flags & JSFUN_EXPR_CLOSURE) {    if (function->flags & JSFUN_EXPR_CLOSURE) {
396      /* expression closure */      /* expression closure - use output_statement instead of instrument_statement */
397      output_statement(node->pn_body, f, indent + 2, false);      if (node->pn_body->pn_type == TOK_BODY) {
398          assert(node->pn_body->pn_arity == PN_LIST);
399          assert(node->pn_body->pn_count == 2);
400          output_statement(node->pn_body->pn_head->pn_next, f, indent + 2, false);
401        }
402        else {
403          output_statement(node->pn_body, f, indent + 2, false);
404        }
405    }    }
406    else {    else {
407      instrument_statement(node->pn_body, f, indent + 2, false);      assert(node->pn_body->pn_type == TOK_LC);
408        assert(node->pn_body->pn_arity == PN_LIST);
409        JSParseNode * p = node->pn_body->pn_head;
410        if (destructuring) {
411          p = p->pn_next;
412        }
413        for (; p != NULL; p = p->pn_next) {
414          instrument_statement(p, f, indent + 2, false);
415        }
416    }    }
417    
418    Stream_write_string(f, "}\n");    Stream_write_char(f, '}');
419  }  }
420    
421  static void instrument_function_call(JSParseNode * node, Stream * f) {  static void instrument_function_call(JSParseNode * node, Stream * f) {
422    if (node->pn_head->pn_type == TOK_FUNCTION) {    JSParseNode * function_node = node->pn_head;
423      /* it's a generator expression */    if (function_node->pn_type == TOK_FUNCTION) {
424      JSParseNode * function_node = node->pn_head;      JSObject * object = function_node->pn_funpob->object;
425      JSParseNode * lexical_scope_node = function_node->pn_body;      assert(JS_ObjectIsFunction(context, object));
426      assert(lexical_scope_node->pn_type == TOK_LEXICALSCOPE);      JSFunction * function = (JSFunction *) JS_GetPrivate(context, object);
427      assert(lexical_scope_node->pn_arity == PN_NAME);      assert(function);
428      JSParseNode * for_node = lexical_scope_node->pn_body;      assert(object == &function->object);
429      assert(for_node->pn_type == TOK_FOR);  
430      assert(for_node->pn_arity == PN_BINARY);      if (function_node->pn_flags & TCF_GENEXP_LAMBDA) {
431      JSParseNode * if_node = NULL;        /* it's a generator expression */
432      JSParseNode * semi_node;        Stream_write_char(f, '(');
433      switch (for_node->pn_right->pn_type) {        output_array_comprehension_or_generator_expression(function_node->pn_body, f);
     case TOK_SEMI:  
       semi_node = for_node->pn_right;  
       break;  
     case TOK_IF:  
       if_node = for_node->pn_right;  
       assert(if_node->pn_arity == PN_TERNARY);  
       semi_node = if_node->pn_kid2;  
       assert(semi_node->pn_type == TOK_SEMI);  
       break;  
     default:  
       abort();  
       break;  
     }  
     assert(semi_node->pn_arity == PN_UNARY);  
     JSParseNode * yield_node = semi_node->pn_kid;  
     assert(yield_node->pn_type == TOK_YIELD);  
     Stream_write_char(f, '(');  
     instrument_expression(yield_node->pn_kid, f);  
     Stream_write_char(f, ' ');  
     output_for_in(for_node, f);  
     if (if_node) {  
       Stream_write_string(f, " if (");  
       instrument_expression(if_node->pn_kid1, f);  
434        Stream_write_char(f, ')');        Stream_write_char(f, ')');
435          return;
436      }      }
     Stream_write_char(f, ')');  
437    }    }
438    else {    output_expression(function_node, f, false);
439      instrument_expression(node->pn_head, f);    Stream_write_char(f, '(');
440      Stream_write_char(f, '(');    for (struct JSParseNode * p = function_node->pn_next; p != NULL; p = p->pn_next) {
441      for (struct JSParseNode * p = node->pn_head->pn_next; p != NULL; p = p->pn_next) {      if (p != node->pn_head->pn_next) {
442        if (p != node->pn_head->pn_next) {        Stream_write_string(f, ", ");
         Stream_write_string(f, ", ");  
       }  
       instrument_expression(p, f);  
443      }      }
444      Stream_write_char(f, ')');      output_expression(p, f, false);
445    }    }
446      Stream_write_char(f, ')');
447  }  }
448    
449  static void instrument_declarations(JSParseNode * list, Stream * f) {  static void instrument_declarations(JSParseNode * list, Stream * f) {
450    assert(list->pn_arity == PN_LIST);    assert(list->pn_arity == PN_LIST);
451    for (JSParseNode * p = list->pn_head; p != NULL; p = p->pn_next) {    for (JSParseNode * p = list->pn_head; p != NULL; p = p->pn_next) {
452      switch (p->pn_type) {      if (p != list->pn_head) {
453      case TOK_NAME:        Stream_write_string(f, ", ");
       assert(p->pn_arity == PN_NAME);  
       if (p != list->pn_head) {  
         Stream_write_string(f, ", ");  
       }  
       print_string_atom(p->pn_atom, f);  
       if (p->pn_expr != NULL) {  
         Stream_write_string(f, " = ");  
         instrument_expression(p->pn_expr, f);  
       }  
       break;  
     case TOK_ASSIGN:  
     case TOK_RB:  
     case TOK_RC:  
       /* destructuring */  
       instrument_expression(p, f);  
       break;  
     default:  
       abort();  
       break;  
454      }      }
455        output_expression(p, f, false);
456    }    }
457  }  }
458    
# Line 425  Line 469 
469  TOK_INSTANCEOF  binary  TOK_INSTANCEOF  binary
470  TOK_IN          binary  TOK_IN          binary
471  */  */
472  static void instrument_expression(JSParseNode * node, Stream * f) {  static void output_expression(JSParseNode * node, Stream * f, bool parenthesize_object_literals) {
473    switch (node->pn_type) {    switch (node->pn_type) {
474    case TOK_FUNCTION:    case TOK_FUNCTION:
475        Stream_write_char(f, '(');
476      instrument_function(node, f, 0, FUNCTION_NORMAL);      instrument_function(node, f, 0, FUNCTION_NORMAL);
477        Stream_write_char(f, ')');
478      break;      break;
479    case TOK_COMMA:    case TOK_COMMA:
480      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) {
481        if (p != node->pn_head) {        if (p != node->pn_head) {
482          Stream_write_string(f, ", ");          Stream_write_string(f, ", ");
483        }        }
484        instrument_expression(p, f);        output_expression(p, f, parenthesize_object_literals);
485      }      }
486      break;      break;
487    case TOK_ASSIGN:    case TOK_ASSIGN:
488      instrument_expression(node->pn_left, f);      output_expression(node->pn_left, f, parenthesize_object_literals);
489      Stream_write_char(f, ' ');      Stream_write_char(f, ' ');
490      switch (node->pn_op) {      switch (node->pn_op) {
491      case JSOP_ADD:      case JSOP_ADD:
# Line 460  Line 506 
506        break;        break;
507      }      }
508      Stream_write_string(f, "= ");      Stream_write_string(f, "= ");
509      instrument_expression(node->pn_right, f);      output_expression(node->pn_right, f, false);
510      break;      break;
511    case TOK_HOOK:    case TOK_HOOK:
512      instrument_expression(node->pn_kid1, f);      output_expression(node->pn_kid1, f, parenthesize_object_literals);
513      Stream_write_string(f, "? ");      Stream_write_string(f, "? ");
514      instrument_expression(node->pn_kid2, f);      output_expression(node->pn_kid2, f, false);
515      Stream_write_string(f, ": ");      Stream_write_string(f, ": ");
516      instrument_expression(node->pn_kid3, f);      output_expression(node->pn_kid3, f, false);
517      break;      break;
518    case TOK_OR:    case TOK_OR:
519    case TOK_AND:    case TOK_AND:
# Line 483  Line 529 
529    case TOK_DIVOP:    case TOK_DIVOP:
530      switch (node->pn_arity) {      switch (node->pn_arity) {
531      case PN_BINARY:      case PN_BINARY:
532        instrument_expression(node->pn_left, f);        output_expression(node->pn_left, f, parenthesize_object_literals);
533        Stream_printf(f, " %s ", get_op(node->pn_op));        Stream_printf(f, " %s ", get_op(node->pn_op));
534        instrument_expression(node->pn_right, f);        output_expression(node->pn_right, f, false);
535        break;        break;
536      case PN_LIST:      case PN_LIST:
537        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) {
538          if (p != node->pn_head) {          if (p == node->pn_head) {
539              output_expression(p, f, parenthesize_object_literals);
540            }
541            else {
542            Stream_printf(f, " %s ", get_op(node->pn_op));            Stream_printf(f, " %s ", get_op(node->pn_op));
543              output_expression(p, f, false);
544          }          }
         instrument_expression(p, f);  
545        }        }
546        break;        break;
547      default:      default:
# Line 502  Line 551 
551    case TOK_UNARYOP:    case TOK_UNARYOP:
552      switch (node->pn_op) {      switch (node->pn_op) {
553      case JSOP_NEG:      case JSOP_NEG:
554        Stream_write_char(f, '-');        Stream_write_string(f, "- ");
555        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, false);
556        break;        break;
557      case JSOP_POS:      case JSOP_POS:
558        Stream_write_char(f, '+');        Stream_write_string(f, "+ ");
559        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, false);
560        break;        break;
561      case JSOP_NOT:      case JSOP_NOT:
562        Stream_write_char(f, '!');        Stream_write_string(f, "! ");
563        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, false);
564        break;        break;
565      case JSOP_BITNOT:      case JSOP_BITNOT:
566        Stream_write_char(f, '~');        Stream_write_string(f, "~ ");
567        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, false);
568        break;        break;
569      case JSOP_TYPEOF:      case JSOP_TYPEOF:
570        Stream_write_string(f, "typeof ");        Stream_write_string(f, "typeof ");
571        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, false);
572        break;        break;
573      case JSOP_VOID:      case JSOP_VOID:
574        Stream_write_string(f, "void ");        Stream_write_string(f, "void ");
575        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, false);
576        break;        break;
577      default:      default:
578        abort();        fatal_source(file_id, node->pn_pos.begin.lineno, "unknown operator (%d)", node->pn_op);
579        break;        break;
580      }      }
581      break;      break;
# Line 540  Line 589 
589      case JSOP_INCPROP:      case JSOP_INCPROP:
590      case JSOP_INCELEM:      case JSOP_INCELEM:
591        Stream_write_string(f, "++");        Stream_write_string(f, "++");
592        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, false);
593        break;        break;
594      case JSOP_DECNAME:      case JSOP_DECNAME:
595      case JSOP_DECPROP:      case JSOP_DECPROP:
596      case JSOP_DECELEM:      case JSOP_DECELEM:
597        Stream_write_string(f, "--");        Stream_write_string(f, "--");
598        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, false);
599        break;        break;
600      case JSOP_NAMEINC:      case JSOP_NAMEINC:
601      case JSOP_PROPINC:      case JSOP_PROPINC:
602      case JSOP_ELEMINC:      case JSOP_ELEMINC:
603        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, parenthesize_object_literals);
604        Stream_write_string(f, "++");        Stream_write_string(f, "++");
605        break;        break;
606      case JSOP_NAMEDEC:      case JSOP_NAMEDEC:
607      case JSOP_PROPDEC:      case JSOP_PROPDEC:
608      case JSOP_ELEMDEC:      case JSOP_ELEMDEC:
609        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, parenthesize_object_literals);
610        Stream_write_string(f, "--");        Stream_write_string(f, "--");
611        break;        break;
612      default:      default:
# Line 571  Line 620 
620      break;      break;
621    case TOK_DELETE:    case TOK_DELETE:
622      Stream_write_string(f, "delete ");      Stream_write_string(f, "delete ");
623      instrument_expression(node->pn_kid, f);      output_expression(node->pn_kid, f, false);
624      break;      break;
625    case TOK_DOT:    case TOK_DOT:
626        /* numeric literals must be parenthesized */
627        switch (node->pn_expr->pn_type) {
628        case TOK_NUMBER:
629          Stream_write_char(f, '(');
630          output_expression(node->pn_expr, f, false);
631          Stream_write_char(f, ')');
632          break;
633        default:
634          output_expression(node->pn_expr, f, true);
635          break;
636        }
637      /*      /*
638      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'
639      contains illegal characters, we have to use the subscript syntax instead of      contains illegal characters, we have to use the subscript syntax instead of
640      the dot syntax.      the dot syntax.
641      */      */
     instrument_expression(node->pn_expr, f);  
642      assert(ATOM_IS_STRING(node->pn_atom));      assert(ATOM_IS_STRING(node->pn_atom));
643      {      {
644        JSString * s = ATOM_TO_STRING(node->pn_atom);        JSString * s = ATOM_TO_STRING(node->pn_atom);
# Line 608  Line 667 
667      }      }
668      break;      break;
669    case TOK_LB:    case TOK_LB:
670      instrument_expression(node->pn_left, f);      output_expression(node->pn_left, f, false);
671      Stream_write_char(f, '[');      Stream_write_char(f, '[');
672      instrument_expression(node->pn_right, f);      output_expression(node->pn_right, f, false);
673      Stream_write_char(f, ']');      Stream_write_char(f, ']');
674      break;      break;
675    case TOK_LP:    case TOK_LP:
# Line 624  Line 683 
683        }        }
684        /* TOK_COMMA is a special case: a hole in the array */        /* TOK_COMMA is a special case: a hole in the array */
685        if (p->pn_type != TOK_COMMA) {        if (p->pn_type != TOK_COMMA) {
686          instrument_expression(p, f);          output_expression(p, f, false);
687        }        }
688      }      }
689      if (node->pn_extra == PNX_ENDCOMMA) {      if (node->pn_extra == PNX_ENDCOMMA) {
# Line 633  Line 692 
692      Stream_write_char(f, ']');      Stream_write_char(f, ']');
693      break;      break;
694    case TOK_RC:    case TOK_RC:
695        if (parenthesize_object_literals) {
696          Stream_write_char(f, '(');
697        }
698      Stream_write_char(f, '{');      Stream_write_char(f, '{');
699      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) {
700        assert(p->pn_type == TOK_COLON);        if (p->pn_type != TOK_COLON) {
701            fatal_source(file_id, p->pn_pos.begin.lineno, "unsupported node type (%d)", p->pn_type);
702          }
703        if (p != node->pn_head) {        if (p != node->pn_head) {
704          Stream_write_string(f, ", ");          Stream_write_string(f, ", ");
705        }        }
# Line 650  Line 714 
714          else {          else {
715            Stream_write_string(f, "set ");            Stream_write_string(f, "set ");
716          }          }
717          instrument_expression(p->pn_left, f);          output_expression(p->pn_left, f, false);
718          if (p->pn_right->pn_type != TOK_FUNCTION) {          if (p->pn_right->pn_type != TOK_FUNCTION) {
719            fatal("parse error: expected function");            fatal_source(file_id, p->pn_pos.begin.lineno, "expected function");
720          }          }
721          instrument_function(p->pn_right, f, 0, FUNCTION_GETTER_OR_SETTER);          instrument_function(p->pn_right, f, 0, FUNCTION_GETTER_OR_SETTER);
722          break;          break;
723        default:        default:
724          instrument_expression(p->pn_left, f);          output_expression(p->pn_left, f, false);
725          Stream_write_string(f, ": ");          Stream_write_string(f, ": ");
726          instrument_expression(p->pn_right, f);          output_expression(p->pn_right, f, false);
727          break;          break;
728        }        }
729      }      }
730      Stream_write_char(f, '}');      Stream_write_char(f, '}');
731        if (parenthesize_object_literals) {
732          Stream_write_char(f, ')');
733        }
734      break;      break;
735    case TOK_RP:    case TOK_RP:
736      Stream_write_char(f, '(');      Stream_write_char(f, '(');
737      instrument_expression(node->pn_kid, f);      output_expression(node->pn_kid, f, false);
738      Stream_write_char(f, ')');      Stream_write_char(f, ')');
739      break;      break;
740    case TOK_NAME:    case TOK_NAME:
741      print_string_atom(node->pn_atom, f);      print_string_atom(node->pn_atom, f);
742        if (node->pn_expr != NULL) {
743          Stream_write_string(f, " = ");
744          output_expression(node->pn_expr, f, false);
745        }
746      break;      break;
747    case TOK_STRING:    case TOK_STRING:
748      print_quoted_string_atom(node->pn_atom, f);      print_quoted_string_atom(node->pn_atom, f);
# Line 693  Line 764 
764      To keep the output simple, special-case zero.      To keep the output simple, special-case zero.
765      */      */
766      if (node->pn_dval == 0.0) {      if (node->pn_dval == 0.0) {
767        Stream_write_string(f, "0");        if (signbit(node->pn_dval)) {
768            Stream_write_string(f, "-0");
769          }
770          else {
771            Stream_write_string(f, "0");
772          }
773        }
774        else if (node->pn_dval == INFINITY) {
775          Stream_write_string(f, "Number.POSITIVE_INFINITY");
776        }
777        else if (node->pn_dval == -INFINITY) {
778          Stream_write_string(f, "Number.NEGATIVE_INFINITY");
779        }
780        else if (isnan(node->pn_dval)) {
781          Stream_write_string(f, "Number.NaN");
782      }      }
783      else {      else {
784        Stream_printf(f, "%.15g", node->pn_dval);        Stream_printf(f, "%.15g", node->pn_dval);
# Line 719  Line 804 
804      }      }
805      break;      break;
806    case TOK_INSTANCEOF:    case TOK_INSTANCEOF:
807      instrument_expression(node->pn_left, f);      output_expression(node->pn_left, f, parenthesize_object_literals);
808      Stream_write_string(f, " instanceof ");      Stream_write_string(f, " instanceof ");
809      instrument_expression(node->pn_right, f);      output_expression(node->pn_right, f, false);
810      break;      break;
811    case TOK_IN:    case TOK_IN:
812      instrument_expression(node->pn_left, f);      output_expression(node->pn_left, f, false);
813      Stream_write_string(f, " in ");      Stream_write_string(f, " in ");
814      instrument_expression(node->pn_right, f);      output_expression(node->pn_right, f, false);
815      break;      break;
816    case TOK_LEXICALSCOPE:    case TOK_LEXICALSCOPE:
817      assert(node->pn_arity == PN_NAME);      assert(node->pn_arity == PN_NAME);
# Line 737  Line 822 
822      assert(node->pn_expr->pn_left->pn_arity == PN_LIST);      assert(node->pn_expr->pn_left->pn_arity == PN_LIST);
823      instrument_declarations(node->pn_expr->pn_left, f);      instrument_declarations(node->pn_expr->pn_left, f);
824      Stream_write_string(f, ") ");      Stream_write_string(f, ") ");
825      instrument_expression(node->pn_expr->pn_right, f);      output_expression(node->pn_expr->pn_right, f, true);
826      break;      break;
827    case TOK_YIELD:    case TOK_YIELD:
828      assert(node->pn_arity == PN_UNARY);      assert(node->pn_arity == PN_UNARY);
829      Stream_write_string(f, "yield ");      Stream_write_string(f, "yield");
830      instrument_expression(node->pn_kid, f);      if (node->pn_kid != NULL) {
831          Stream_write_char(f, ' ');
832          output_expression(node->pn_kid, f, true);
833        }
834      break;      break;
835    case TOK_ARRAYCOMP:    case TOK_ARRAYCOMP:
836      assert(node->pn_arity == PN_LIST);      assert(node->pn_arity == PN_LIST);
# Line 759  Line 847 
847          abort();          abort();
848          break;          break;
849        }        }
       assert(block_node->pn_type == TOK_LEXICALSCOPE);  
       assert(block_node->pn_arity == PN_NAME);  
       JSParseNode * for_node = block_node->pn_expr;  
       assert(for_node->pn_type == TOK_FOR);  
       assert(for_node->pn_arity == PN_BINARY);  
       JSParseNode * if_node = NULL;  
       JSParseNode * push_node;  
       switch (for_node->pn_right->pn_type) {  
       case TOK_ARRAYPUSH:  
         push_node = for_node->pn_right;  
         assert(push_node->pn_arity == PN_UNARY);  
         break;  
       case TOK_IF:  
         if_node = for_node->pn_right;  
         assert(if_node->pn_arity == PN_TERNARY);  
         push_node = if_node->pn_kid2;  
         break;  
       default:  
         abort();  
         break;  
       }  
850        Stream_write_char(f, '[');        Stream_write_char(f, '[');
851        instrument_expression(push_node->pn_kid, f);        output_array_comprehension_or_generator_expression(block_node, f);
       Stream_write_char(f, ' ');  
       output_for_in(for_node, f);  
       if (if_node) {  
         Stream_write_string(f, " if (");  
         instrument_expression(if_node->pn_kid1, f);  
         Stream_write_char(f, ')');  
       }  
852        Stream_write_char(f, ']');        Stream_write_char(f, ']');
853      }      }
854      break;      break;
# Line 803  Line 863 
863      instrument_declarations(node, f);      instrument_declarations(node, f);
864      break;      break;
865    default:    default:
866      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);
867    }    }
868  }  }
869    
# Line 811  Line 871 
871    switch (node->pn_type) {    switch (node->pn_type) {
872    case TOK_FUNCTION:    case TOK_FUNCTION:
873      instrument_function(node, f, indent, FUNCTION_NORMAL);      instrument_function(node, f, indent, FUNCTION_NORMAL);
874        Stream_write_char(f, '\n');
875      break;      break;
876    case TOK_LC:    case TOK_LC:
877      assert(node->pn_arity == PN_LIST);      assert(node->pn_arity == PN_LIST);
# Line 832  Line 893 
893      uint16_t line = node->pn_pos.begin.lineno;      uint16_t line = node->pn_pos.begin.lineno;
894      if (! is_jscoverage_if) {      if (! is_jscoverage_if) {
895        if (line > num_lines) {        if (line > num_lines) {
896          fatal("%s: script contains more than 65,535 lines", file_id);          fatal("file %s contains more than 65,535 lines", file_id);
897        }        }
898        if (line >= 2 && exclusive_directives[line - 2]) {        if (line >= 2 && exclusive_directives[line - 2]) {
899          is_jscoverage_if = true;          is_jscoverage_if = true;
# Line 841  Line 902 
902    
903      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
904      Stream_write_string(f, "if (");      Stream_write_string(f, "if (");
905      instrument_expression(node->pn_kid1, f);      output_expression(node->pn_kid1, f, false);
906      Stream_write_string(f, ") {\n");      Stream_write_string(f, ") {\n");
907      if (is_jscoverage_if && node->pn_kid3) {      if (is_jscoverage_if && node->pn_kid3) {
908        uint16_t else_start = node->pn_kid3->pn_pos.begin.lineno;        uint16_t else_start = node->pn_kid3->pn_pos.begin.lineno;
# Line 878  Line 939 
939      assert(node->pn_arity == PN_BINARY);      assert(node->pn_arity == PN_BINARY);
940      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
941      Stream_write_string(f, "switch (");      Stream_write_string(f, "switch (");
942      instrument_expression(node->pn_left, f);      output_expression(node->pn_left, f, false);
943      Stream_write_string(f, ") {\n");      Stream_write_string(f, ") {\n");
944      for (struct JSParseNode * p = node->pn_right->pn_head; p != NULL; p = p->pn_next) {      {
945        Stream_printf(f, "%*s", indent, "");        JSParseNode * list = node->pn_right;
946        switch (p->pn_type) {        if (list->pn_type == TOK_LEXICALSCOPE) {
947        case TOK_CASE:          list = list->pn_expr;
948          Stream_write_string(f, "case ");        }
949          instrument_expression(p->pn_left, f);        for (struct JSParseNode * p = list->pn_head; p != NULL; p = p->pn_next) {
950          Stream_write_string(f, ":\n");          Stream_printf(f, "%*s", indent, "");
951          break;          switch (p->pn_type) {
952        case TOK_DEFAULT:          case TOK_CASE:
953          Stream_write_string(f, "default:\n");            Stream_write_string(f, "case ");
954          break;            output_expression(p->pn_left, f, false);
955        default:            Stream_write_string(f, ":\n");
956          abort();            break;
957          break;          case TOK_DEFAULT:
958              Stream_write_string(f, "default:\n");
959              break;
960            default:
961              abort();
962              break;
963            }
964            instrument_statement(p->pn_right, f, indent + 2, false);
965        }        }
       instrument_statement(p->pn_right, f, indent + 2, false);  
966      }      }
967      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
968      Stream_write_string(f, "}\n");      Stream_write_string(f, "}\n");
# Line 908  Line 975 
975      assert(node->pn_arity == PN_BINARY);      assert(node->pn_arity == PN_BINARY);
976      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
977      Stream_write_string(f, "while (");      Stream_write_string(f, "while (");
978      instrument_expression(node->pn_left, f);      output_expression(node->pn_left, f, false);
979      Stream_write_string(f, ") {\n");      Stream_write_string(f, ") {\n");
980      instrument_statement(node->pn_right, f, indent + 2, false);      instrument_statement(node->pn_right, f, indent + 2, false);
981      Stream_write_string(f, "}\n");      Stream_write_string(f, "}\n");
# Line 921  Line 988 
988      Stream_write_string(f, "}\n");      Stream_write_string(f, "}\n");
989      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
990      Stream_write_string(f, "while (");      Stream_write_string(f, "while (");
991      instrument_expression(node->pn_right, f);      output_expression(node->pn_right, f, false);
992      Stream_write_string(f, ");\n");      Stream_write_string(f, ");\n");
993      break;      break;
994    case TOK_FOR:    case TOK_FOR:
# Line 938  Line 1005 
1005        assert(node->pn_left->pn_arity == PN_TERNARY);        assert(node->pn_left->pn_arity == PN_TERNARY);
1006        Stream_write_string(f, "for (");        Stream_write_string(f, "for (");
1007        if (node->pn_left->pn_kid1) {        if (node->pn_left->pn_kid1) {
1008          instrument_expression(node->pn_left->pn_kid1, f);          output_expression(node->pn_left->pn_kid1, f, false);
1009        }        }
1010        Stream_write_string(f, ";");        Stream_write_string(f, ";");
1011        if (node->pn_left->pn_kid2) {        if (node->pn_left->pn_kid2) {
1012          Stream_write_char(f, ' ');          Stream_write_char(f, ' ');
1013          instrument_expression(node->pn_left->pn_kid2, f);          output_expression(node->pn_left->pn_kid2, f, false);
1014        }        }
1015        Stream_write_string(f, ";");        Stream_write_string(f, ";");
1016        if (node->pn_left->pn_kid3) {        if (node->pn_left->pn_kid3) {
1017          Stream_write_char(f, ' ');          Stream_write_char(f, ' ');
1018          instrument_expression(node->pn_left->pn_kid3, f);          output_expression(node->pn_left->pn_kid3, f, false);
1019        }        }
1020        Stream_write_char(f, ')');        Stream_write_char(f, ')');
1021        break;        break;
# Line 964  Line 1031 
1031      assert(node->pn_arity == PN_UNARY);      assert(node->pn_arity == PN_UNARY);
1032      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
1033      Stream_write_string(f, "throw ");      Stream_write_string(f, "throw ");
1034      instrument_expression(node->pn_u.unary.kid, f);      output_expression(node->pn_u.unary.kid, f, false);
1035      Stream_write_string(f, ";\n");      Stream_write_string(f, ";\n");
1036      break;      break;
1037    case TOK_TRY:    case TOK_TRY:
# Line 981  Line 1048 
1048          assert(catch->pn_type == TOK_CATCH);          assert(catch->pn_type == TOK_CATCH);
1049          Stream_printf(f, "%*s", indent, "");          Stream_printf(f, "%*s", indent, "");
1050          Stream_write_string(f, "catch (");          Stream_write_string(f, "catch (");
1051          /* this may not be a name - destructuring assignment */          output_expression(catch->pn_kid1, f, false);
         /*  
         assert(catch->pn_kid1->pn_arity == PN_NAME);  
         print_string_atom(catch->pn_kid1->pn_atom, f);  
         */  
         instrument_expression(catch->pn_kid1, f);  
1052          if (catch->pn_kid2) {          if (catch->pn_kid2) {
1053            Stream_write_string(f, " if ");            Stream_write_string(f, " if ");
1054            instrument_expression(catch->pn_kid2, f);            output_expression(catch->pn_kid2, f, false);
1055          }          }
1056          Stream_write_string(f, ") {\n");          Stream_write_string(f, ") {\n");
1057          instrument_statement(catch->pn_kid3, f, indent + 2, false);          instrument_statement(catch->pn_kid3, f, indent + 2, false);
# Line 1024  Line 1086 
1086      assert(node->pn_arity == PN_BINARY);      assert(node->pn_arity == PN_BINARY);
1087      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
1088      Stream_write_string(f, "with (");      Stream_write_string(f, "with (");
1089      instrument_expression(node->pn_left, f);      output_expression(node->pn_left, f, false);
1090      Stream_write_string(f, ") {\n");      Stream_write_string(f, ") {\n");
1091      instrument_statement(node->pn_right, f, indent + 2, false);      instrument_statement(node->pn_right, f, indent + 2, false);
1092      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
# Line 1032  Line 1094 
1094      break;      break;
1095    case TOK_VAR:    case TOK_VAR:
1096      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
1097      instrument_expression(node, f);      output_expression(node, f, false);
1098      Stream_write_string(f, ";\n");      Stream_write_string(f, ";\n");
1099      break;      break;
1100    case TOK_RETURN:    case TOK_RETURN:
# Line 1041  Line 1103 
1103      Stream_write_string(f, "return");      Stream_write_string(f, "return");
1104      if (node->pn_kid != NULL) {      if (node->pn_kid != NULL) {
1105        Stream_write_char(f, ' ');        Stream_write_char(f, ' ');
1106        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, true);
1107      }      }
1108      Stream_write_string(f, ";\n");      Stream_write_string(f, ";\n");
1109      break;      break;
# Line 1049  Line 1111 
1111      assert(node->pn_arity == PN_UNARY);      assert(node->pn_arity == PN_UNARY);
1112      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
1113      if (node->pn_kid != NULL) {      if (node->pn_kid != NULL) {
1114        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, true);
1115      }      }
1116      Stream_write_string(f, ";\n");      Stream_write_string(f, ";\n");
1117      break;      break;
1118    case TOK_COLON:    case TOK_COLON:
1119      {
1120      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 ...  
     */  
1121      Stream_printf(f, "%*s", indent < 2? 0: indent - 2, "");      Stream_printf(f, "%*s", indent < 2? 0: indent - 2, "");
1122      print_string_atom(node->pn_atom, f);      print_string_atom(node->pn_atom, f);
1123      Stream_write_string(f, ":\n");      Stream_write_string(f, ":\n");
1124      /*      JSParseNode * labelled = node->pn_expr;
1125      ... use output_statement instead of instrument_statement.      if (labelled->pn_type == TOK_LEXICALSCOPE) {
1126      */        labelled = labelled->pn_expr;
1127      output_statement(node->pn_expr, f, indent, false);      }
1128        if (labelled->pn_type == TOK_LC) {
1129          /* labelled block */
1130          Stream_printf(f, "%*s", indent, "");
1131          Stream_write_string(f, "{\n");
1132          instrument_statement(labelled, f, indent + 2, false);
1133          Stream_printf(f, "%*s", indent, "");
1134          Stream_write_string(f, "}\n");
1135        }
1136        else {
1137          /*
1138          This one is tricky: can't output instrumentation between the label and the
1139          statement it's supposed to label, so use output_statement instead of
1140          instrument_statement.
1141          */
1142          output_statement(labelled, f, indent, false);
1143        }
1144      break;      break;
1145      }
1146    case TOK_LEXICALSCOPE:    case TOK_LEXICALSCOPE:
1147      /* let statement */      /* let statement */
1148      assert(node->pn_arity == PN_NAME);      assert(node->pn_arity == PN_NAME);
# Line 1109  Line 1185 
1185      case PN_LIST:      case PN_LIST:
1186        /* let definition */        /* let definition */
1187        Stream_printf(f, "%*s", indent, "");        Stream_printf(f, "%*s", indent, "");
1188        instrument_expression(node, f);        output_expression(node, f, false);
1189        Stream_write_string(f, ";\n");        Stream_write_string(f, ";\n");
1190        break;        break;
1191      default:      default:
# Line 1122  Line 1198 
1198      Stream_write_string(f, "debugger;\n");      Stream_write_string(f, "debugger;\n");
1199      break;      break;
1200    default:    default:
1201      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);
1202    }    }
1203  }  }
1204    
# Line 1135  Line 1211 
1211    if (node->pn_type != TOK_LC && node->pn_type != TOK_LEXICALSCOPE) {    if (node->pn_type != TOK_LC && node->pn_type != TOK_LEXICALSCOPE) {
1212      uint16_t line = node->pn_pos.begin.lineno;      uint16_t line = node->pn_pos.begin.lineno;
1213      if (line > num_lines) {      if (line > num_lines) {
1214        fatal("%s: script contains more than 65,535 lines", file_id);        fatal("file %s contains more than 65,535 lines", file_id);
1215      }      }
1216    
1217      /* the root node has line number 0 */      /* the root node has line number 0 */
# Line 1183  Line 1259 
1259  }  }
1260    
1261  static void error_reporter(JSContext * context, const char * message, JSErrorReport * report) {  static void error_reporter(JSContext * context, const char * message, JSErrorReport * report) {
1262    fprintf(stderr, "jscoverage: parse error: line %u: %s\n", report->lineno, message);    warn_source(file_id, report->lineno, "%s", message);
1263  }  }
1264    
1265  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) {
# Line 1192  Line 1268 
1268    /* parse the javascript */    /* parse the javascript */
1269    JSParseContext parse_context;    JSParseContext parse_context;
1270    if (! js_InitParseContext(context, &parse_context, NULL, NULL, characters, num_characters, NULL, NULL, 1)) {    if (! js_InitParseContext(context, &parse_context, NULL, NULL, characters, num_characters, NULL, NULL, 1)) {
1271      fatal("cannot create token stream from file: %s", file_id);      fatal("cannot create token stream from file %s", file_id);
1272    }    }
1273    JSErrorReporter old_error_reporter = JS_SetErrorReporter(context, error_reporter);    JSErrorReporter old_error_reporter = JS_SetErrorReporter(context, error_reporter);
1274    JSParseNode * node = js_ParseScript(context, global, &parse_context);    JSParseNode * node = js_ParseScript(context, global, &parse_context);
1275    if (node == NULL) {    if (node == NULL) {
1276      js_ReportUncaughtException(context);      js_ReportUncaughtException(context);
1277      fatal("parse error in file: %s", file_id);      fatal("parse error in file %s", file_id);
1278    }    }
1279    JS_SetErrorReporter(context, old_error_reporter);    JS_SetErrorReporter(context, old_error_reporter);
1280    num_lines = node->pn_pos.end.lineno;    num_lines = node->pn_pos.end.lineno;
# Line 1219  Line 1295 
1295    size_t i = 0;    size_t i = 0;
1296    while (i < num_characters) {    while (i < num_characters) {
1297      if (line_number == UINT16_MAX) {      if (line_number == UINT16_MAX) {
1298        fatal("%s: script has more than 65,535 lines", file_id);        fatal("file %s contains more than 65,535 lines", file_id);
1299      }      }
1300      line_number++;      line_number++;
1301      size_t line_start = i;      size_t line_start = i;
# Line 1286  Line 1362 
1362    
1363    /* write line number info to the output */    /* write line number info to the output */
1364    Stream_write_string(output, "/* automatically generated by JSCoverage - do not edit */\n");    Stream_write_string(output, "/* automatically generated by JSCoverage - do not edit */\n");
1365    Stream_write_string(output, "if (! top._$jscoverage) {\n  top._$jscoverage = {};\n}\n");    if (jscoverage_mozilla) {
1366    Stream_write_string(output, "var _$jscoverage = top._$jscoverage;\n");      Stream_write_string(output, "try {\n");
1367        Stream_write_string(output, "  Components.utils.import('resource://gre/modules/jscoverage.jsm');\n");
1368        Stream_printf(output, "  dump('%s: successfully imported jscoverage module\\n');\n", id);
1369        Stream_write_string(output, "}\n");
1370        Stream_write_string(output, "catch (e) {\n");
1371        Stream_write_string(output, "  _$jscoverage = {};\n");
1372        Stream_printf(output, "  dump('%s: failed to import jscoverage module - coverage not available for this file\\n');\n", id);
1373        Stream_write_string(output, "}\n");
1374      }
1375      else {
1376        Stream_write_string(output, "if (! top._$jscoverage) {\n  top._$jscoverage = {};\n}\n");
1377        Stream_write_string(output, "var _$jscoverage = top._$jscoverage;\n");
1378      }
1379    Stream_printf(output, "if (! _$jscoverage['%s']) {\n", file_id);    Stream_printf(output, "if (! _$jscoverage['%s']) {\n", file_id);
1380    Stream_printf(output, "  _$jscoverage['%s'] = [];\n", file_id);    Stream_printf(output, "  _$jscoverage['%s'] = [];\n", file_id);
1381    for (int i = 0; i < num_lines; i++) {    for (int i = 0; i < num_lines; i++) {

Legend:
Removed from v.349  
changed lines
  Added in v.391

  ViewVC Help
Powered by ViewVC 1.1.24