/[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 346 by siliconforks, Fri Oct 24 16:16:24 2008 UTC revision 379 by siliconforks, Tue Oct 28 17:50:42 2008 UTC
# Line 28  Line 28 
28  #include <jsapi.h>  #include <jsapi.h>
29  #include <jsarena.h>  #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>
# Line 51  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;
# Line 67  Line 70 
70  static uint16_t num_lines = 0;  static uint16_t num_lines = 0;
71    
72  void jscoverage_set_js_version(const char * version) {  void jscoverage_set_js_version(const char * version) {
73    js_version = atoi(version);    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) {
# Line 255  Line 267 
267    }    }
268  }  }
269    
270  static void instrument_expression(JSParseNode * node, Stream * f);  static void output_expression(JSParseNode * node, Stream * f, bool parenthesize_object_literals);
271  static void instrument_statement(JSParseNode * node, Stream * f, int indent, 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);  static void output_statement(JSParseNode * node, Stream * f, int indent, bool is_jscoverage_if);
273    
# Line 272  Line 284 
284      Stream_write_string(f, "each ");      Stream_write_string(f, "each ");
285    }    }
286    Stream_write_char(f, '(');    Stream_write_char(f, '(');
287    instrument_expression(node->pn_left, f);    output_expression(node->pn_left, f, false);
288    Stream_write_char(f, ')');    Stream_write_char(f, ')');
289  }  }
290    
291    static void output_array_comprehension_or_generator_expression(JSParseNode * node, Stream * f) {
292      assert(node->pn_type == TOK_LEXICALSCOPE);
293      assert(node->pn_arity == PN_NAME);
294      JSParseNode * for_node = node->pn_expr;
295      assert(for_node->pn_type == TOK_FOR);
296      assert(for_node->pn_arity == PN_BINARY);
297      JSParseNode * p = for_node;
298      while (p->pn_type == TOK_FOR) {
299        p = p->pn_right;
300      }
301      JSParseNode * if_node = NULL;
302      if (p->pn_type == TOK_IF) {
303        if_node = p;
304        assert(if_node->pn_arity == PN_TERNARY);
305        p = if_node->pn_kid2;
306      }
307      assert(p->pn_arity == PN_UNARY);
308      p = p->pn_kid;
309      if (p->pn_type == TOK_YIELD) {
310        /* for generator expressions */
311        p = p->pn_kid;
312      }
313    
314      output_expression(p, f, false);
315      p = for_node;
316      while (p->pn_type == TOK_FOR) {
317        Stream_write_char(f, ' ');
318        output_for_in(p, f);
319        p = p->pn_right;
320      }
321      if (if_node) {
322        Stream_write_string(f, " if (");
323        output_expression(if_node->pn_kid1, f, false);
324        Stream_write_char(f, ')');
325      }
326    }
327    
328  static void instrument_function(JSParseNode * node, Stream * f, int indent, enum FunctionType type) {  static void instrument_function(JSParseNode * node, Stream * f, int indent, enum FunctionType type) {
329    assert(node->pn_type == TOK_FUNCTION);    assert(node->pn_type == TOK_FUNCTION);
330    assert(node->pn_arity == PN_FUNC);    assert(node->pn_arity == PN_FUNC);
# Line 286  Line 335 
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    
# Line 299  Line 347 
347    function parameters - see JS_DecompileFunction in jsapi.cpp, which calls    function parameters - see JS_DecompileFunction in jsapi.cpp, which calls
348    js_DecompileFunction in jsopcode.cpp    js_DecompileFunction in jsopcode.cpp
349    */    */
350    Stream_write_string(f, "(");    Stream_write_char(f, '(');
351    JSArenaPool pool;    JSArenaPool pool;
352    JS_INIT_ARENA_POOL(&pool, "instrument_function", 256, 1, &context->scriptStackQuota);    JS_INIT_ARENA_POOL(&pool, "instrument_function", 256, 1, &context->scriptStackQuota);
353    jsuword * local_names = NULL;    jsuword * local_names = NULL;
# Line 309  Line 357 
357        fatal("out of memory");        fatal("out of memory");
358      }      }
359    }    }
360      bool destructuring = false;
361    for (int i = 0; i < function->nargs; i++) {    for (int i = 0; i < function->nargs; i++) {
362      if (i > 0) {      if (i > 0) {
363        Stream_write_string(f, ", ");        Stream_write_string(f, ", ");
364      }      }
365      JSAtom * param = JS_LOCAL_NAME_TO_ATOM(local_names[i]);      JSAtom * param = JS_LOCAL_NAME_TO_ATOM(local_names[i]);
366      print_string_atom(param, f);      if (param == NULL) {
367          destructuring = true;
368          JSParseNode * expression = NULL;
369          assert(node->pn_body->pn_type == TOK_LC || node->pn_body->pn_type == TOK_BODY);
370          JSParseNode * semi = node->pn_body->pn_head;
371          assert(semi->pn_type == TOK_SEMI);
372          JSParseNode * comma = semi->pn_kid;
373          assert(comma->pn_type == TOK_COMMA);
374          for (JSParseNode * p = comma->pn_head; p != NULL; p = p->pn_next) {
375            assert(p->pn_type == TOK_ASSIGN);
376            JSParseNode * rhs = p->pn_right;
377            assert(JSSTRING_LENGTH(ATOM_TO_STRING(rhs->pn_atom)) == 0);
378            if (rhs->pn_slot == i) {
379              expression = p->pn_left;
380              break;
381            }
382          }
383          assert(expression != NULL);
384          output_expression(expression, f, false);
385        }
386        else {
387          print_string_atom(param, f);
388        }
389    }    }
390    JS_FinishArenaPool(&pool);    JS_FinishArenaPool(&pool);
391    Stream_write_string(f, ") {\n");    Stream_write_string(f, ") {\n");
392    
393    /* function body */    /* function body */
394    if (function->flags & JSFUN_EXPR_CLOSURE) {    if (function->flags & JSFUN_EXPR_CLOSURE) {
395      /* expression closure */      /* expression closure - use output_statement instead of instrument_statement */
396      output_statement(node->pn_body, f, indent + 2, false);      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 {    else {
406      instrument_statement(node->pn_body, f, indent + 2, false);      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    if (node->pn_head->pn_type == TOK_FUNCTION) {    JSParseNode * function_node = node->pn_head;
422      /* it's a generator expression */    if (function_node->pn_type == TOK_FUNCTION) {
423      JSParseNode * function_node = node->pn_head;      JSObject * object = function_node->pn_funpob->object;
424      JSParseNode * lexical_scope_node = function_node->pn_body;      assert(JS_ObjectIsFunction(context, object));
425      assert(lexical_scope_node->pn_type == TOK_LEXICALSCOPE);      JSFunction * function = (JSFunction *) JS_GetPrivate(context, object);
426      assert(lexical_scope_node->pn_arity == PN_NAME);      assert(function);
427      JSParseNode * for_node = lexical_scope_node->pn_body;      assert(object == &function->object);
428      assert(for_node->pn_type == TOK_FOR);  
429      assert(for_node->pn_arity == PN_BINARY);      if (function_node->pn_flags & TCF_GENEXP_LAMBDA) {
430      JSParseNode * if_node = NULL;        /* it's a generator expression */
431      JSParseNode * semi_node;        Stream_write_char(f, '(');
432      switch (for_node->pn_right->pn_type) {        output_array_comprehension_or_generator_expression(function_node->pn_body, f);
433      case TOK_SEMI:        Stream_write_char(f, ')');
434        semi_node = for_node->pn_right;        return;
       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;  
435      }      }
436      assert(semi_node->pn_arity == PN_UNARY);      else {
437      JSParseNode * yield_node = semi_node->pn_kid;        Stream_write_char(f, '(');
438      assert(yield_node->pn_type == TOK_YIELD);        output_expression(function_node, f, false);
     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);  
439        Stream_write_char(f, ')');        Stream_write_char(f, ')');
440      }      }
     Stream_write_char(f, ')');  
441    }    }
442    else {    else {
443      instrument_expression(node->pn_head, f);      output_expression(function_node, f, false);
444      Stream_write_char(f, '(');    }
445      for (struct JSParseNode * p = node->pn_head->pn_next; p != NULL; p = p->pn_next) {    Stream_write_char(f, '(');
446        if (p != node->pn_head->pn_next) {    for (struct JSParseNode * p = function_node->pn_next; p != NULL; p = p->pn_next) {
447          Stream_write_string(f, ", ");      if (p != node->pn_head->pn_next) {
448        }        Stream_write_string(f, ", ");
       instrument_expression(p, f);  
449      }      }
450      Stream_write_char(f, ')');      output_expression(p, f, false);
451    }    }
452      Stream_write_char(f, ')');
453  }  }
454    
455  static void instrument_declarations(JSParseNode * list, Stream * f) {  static void instrument_declarations(JSParseNode * list, Stream * f) {
456    assert(list->pn_arity == PN_LIST);    assert(list->pn_arity == PN_LIST);
457    for (JSParseNode * p = list->pn_head; p != NULL; p = p->pn_next) {    for (JSParseNode * p = list->pn_head; p != NULL; p = p->pn_next) {
458      switch (p->pn_type) {      if (p != list->pn_head) {
459      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;  
460      }      }
461        output_expression(p, f, false);
462    }    }
463  }  }
464    
# Line 425  Line 475 
475  TOK_INSTANCEOF  binary  TOK_INSTANCEOF  binary
476  TOK_IN          binary  TOK_IN          binary
477  */  */
478  static void instrument_expression(JSParseNode * node, Stream * f) {  static void output_expression(JSParseNode * node, Stream * f, bool parenthesize_object_literals) {
479    switch (node->pn_type) {    switch (node->pn_type) {
480    case TOK_FUNCTION:    case TOK_FUNCTION:
481      instrument_function(node, f, 0, FUNCTION_NORMAL);      instrument_function(node, f, 0, FUNCTION_NORMAL);
# Line 435  Line 485 
485        if (p != node->pn_head) {        if (p != node->pn_head) {
486          Stream_write_string(f, ", ");          Stream_write_string(f, ", ");
487        }        }
488        instrument_expression(p, f);        output_expression(p, f, parenthesize_object_literals);
489      }      }
490      break;      break;
491    case TOK_ASSIGN:    case TOK_ASSIGN:
492      instrument_expression(node->pn_left, f);      output_expression(node->pn_left, f, parenthesize_object_literals);
493      Stream_write_char(f, ' ');      Stream_write_char(f, ' ');
494      switch (node->pn_op) {      switch (node->pn_op) {
495      case JSOP_ADD:      case JSOP_ADD:
# Line 460  Line 510 
510        break;        break;
511      }      }
512      Stream_write_string(f, "= ");      Stream_write_string(f, "= ");
513      instrument_expression(node->pn_right, f);      output_expression(node->pn_right, f, false);
514      break;      break;
515    case TOK_HOOK:    case TOK_HOOK:
516      instrument_expression(node->pn_kid1, f);      output_expression(node->pn_kid1, f, parenthesize_object_literals);
517      Stream_write_string(f, "? ");      Stream_write_string(f, "? ");
518      instrument_expression(node->pn_kid2, f);      output_expression(node->pn_kid2, f, false);
519      Stream_write_string(f, ": ");      Stream_write_string(f, ": ");
520      instrument_expression(node->pn_kid3, f);      output_expression(node->pn_kid3, f, false);
521      break;      break;
522    case TOK_OR:    case TOK_OR:
523    case TOK_AND:    case TOK_AND:
# Line 483  Line 533 
533    case TOK_DIVOP:    case TOK_DIVOP:
534      switch (node->pn_arity) {      switch (node->pn_arity) {
535      case PN_BINARY:      case PN_BINARY:
536        instrument_expression(node->pn_left, f);        output_expression(node->pn_left, f, parenthesize_object_literals);
537        Stream_printf(f, " %s ", get_op(node->pn_op));        Stream_printf(f, " %s ", get_op(node->pn_op));
538        instrument_expression(node->pn_right, f);        output_expression(node->pn_right, f, false);
539        break;        break;
540      case PN_LIST:      case PN_LIST:
541        for (struct JSParseNode * p = node->pn_head; p != NULL; p = p->pn_next) {        for (struct JSParseNode * p = node->pn_head; p != NULL; p = p->pn_next) {
542          if (p != node->pn_head) {          if (p == node->pn_head) {
543              output_expression(p, f, parenthesize_object_literals);
544            }
545            else {
546            Stream_printf(f, " %s ", get_op(node->pn_op));            Stream_printf(f, " %s ", get_op(node->pn_op));
547              output_expression(p, f, false);
548          }          }
         instrument_expression(p, f);  
549        }        }
550        break;        break;
551      default:      default:
# Line 503  Line 556 
556      switch (node->pn_op) {      switch (node->pn_op) {
557      case JSOP_NEG:      case JSOP_NEG:
558        Stream_write_char(f, '-');        Stream_write_char(f, '-');
559        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, false);
560        break;        break;
561      case JSOP_POS:      case JSOP_POS:
562        Stream_write_char(f, '+');        Stream_write_char(f, '+');
563        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, false);
564        break;        break;
565      case JSOP_NOT:      case JSOP_NOT:
566        Stream_write_char(f, '!');        Stream_write_char(f, '!');
567        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, false);
568        break;        break;
569      case JSOP_BITNOT:      case JSOP_BITNOT:
570        Stream_write_char(f, '~');        Stream_write_char(f, '~');
571        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, false);
572        break;        break;
573      case JSOP_TYPEOF:      case JSOP_TYPEOF:
574        Stream_write_string(f, "typeof ");        Stream_write_string(f, "typeof ");
575        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, false);
576        break;        break;
577      case JSOP_VOID:      case JSOP_VOID:
578        Stream_write_string(f, "void ");        Stream_write_string(f, "void ");
579        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, false);
580        break;        break;
581      default:      default:
582        abort();        fatal_source(file_id, node->pn_pos.begin.lineno, "unknown operator (%d)", node->pn_op);
583        break;        break;
584      }      }
585      break;      break;
# Line 540  Line 593 
593      case JSOP_INCPROP:      case JSOP_INCPROP:
594      case JSOP_INCELEM:      case JSOP_INCELEM:
595        Stream_write_string(f, "++");        Stream_write_string(f, "++");
596        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, false);
597        break;        break;
598      case JSOP_DECNAME:      case JSOP_DECNAME:
599      case JSOP_DECPROP:      case JSOP_DECPROP:
600      case JSOP_DECELEM:      case JSOP_DECELEM:
601        Stream_write_string(f, "--");        Stream_write_string(f, "--");
602        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, false);
603        break;        break;
604      case JSOP_NAMEINC:      case JSOP_NAMEINC:
605      case JSOP_PROPINC:      case JSOP_PROPINC:
606      case JSOP_ELEMINC:      case JSOP_ELEMINC:
607        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, parenthesize_object_literals);
608        Stream_write_string(f, "++");        Stream_write_string(f, "++");
609        break;        break;
610      case JSOP_NAMEDEC:      case JSOP_NAMEDEC:
611      case JSOP_PROPDEC:      case JSOP_PROPDEC:
612      case JSOP_ELEMDEC:      case JSOP_ELEMDEC:
613        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, parenthesize_object_literals);
614        Stream_write_string(f, "--");        Stream_write_string(f, "--");
615        break;        break;
616      default:      default:
# Line 571  Line 624 
624      break;      break;
625    case TOK_DELETE:    case TOK_DELETE:
626      Stream_write_string(f, "delete ");      Stream_write_string(f, "delete ");
627      instrument_expression(node->pn_kid, f);      output_expression(node->pn_kid, f, false);
628      break;      break;
629    case TOK_DOT:    case TOK_DOT:
630        /* numeric literals must be parenthesized */
631        switch (node->pn_expr->pn_type) {
632        case TOK_NUMBER:
633          Stream_write_char(f, '(');
634          output_expression(node->pn_expr, f, false);
635          Stream_write_char(f, ')');
636          break;
637        default:
638          output_expression(node->pn_expr, f, true);
639          break;
640        }
641      /*      /*
642      This may have originally been x['foo-bar'].  Because the string 'foo-bar'      This may have originally been x['foo-bar'].  Because the string 'foo-bar'
643      contains illegal characters, we have to use the subscript syntax instead of      contains illegal characters, we have to use the subscript syntax instead of
644      the dot syntax.      the dot syntax.
645      */      */
     instrument_expression(node->pn_expr, f);  
646      assert(ATOM_IS_STRING(node->pn_atom));      assert(ATOM_IS_STRING(node->pn_atom));
647      {      {
648        JSString * s = ATOM_TO_STRING(node->pn_atom);        JSString * s = ATOM_TO_STRING(node->pn_atom);
649        bool is_keyword = (js_CheckKeyword(JSSTRING_CHARS(s), JSSTRING_LENGTH(s)) != TOK_EOF);        bool must_quote;
650        if (! is_keyword && js_IsIdentifier(s)) {        if (JSSTRING_LENGTH(s) == 0) {
651          Stream_write_char(f, '.');          must_quote = true;
652          print_string_atom(node->pn_atom, f);        }
653          else if (js_CheckKeyword(JSSTRING_CHARS(s), JSSTRING_LENGTH(s)) != TOK_EOF) {
654            must_quote = true;
655          }
656          else if (! js_IsIdentifier(s)) {
657            must_quote = true;
658        }        }
659        else {        else {
660            must_quote = false;
661          }
662          if (must_quote) {
663          Stream_write_char(f, '[');          Stream_write_char(f, '[');
664          print_quoted_string_atom(node->pn_atom, f);          print_quoted_string_atom(node->pn_atom, f);
665          Stream_write_char(f, ']');          Stream_write_char(f, ']');
666        }        }
667          else {
668            Stream_write_char(f, '.');
669            print_string_atom(node->pn_atom, f);
670          }
671      }      }
672      break;      break;
673    case TOK_LB:    case TOK_LB:
674      instrument_expression(node->pn_left, f);      output_expression(node->pn_left, f, false);
675      Stream_write_char(f, '[');      Stream_write_char(f, '[');
676      instrument_expression(node->pn_right, f);      output_expression(node->pn_right, f, false);
677      Stream_write_char(f, ']');      Stream_write_char(f, ']');
678      break;      break;
679    case TOK_LP:    case TOK_LP:
# Line 612  Line 687 
687        }        }
688        /* TOK_COMMA is a special case: a hole in the array */        /* TOK_COMMA is a special case: a hole in the array */
689        if (p->pn_type != TOK_COMMA) {        if (p->pn_type != TOK_COMMA) {
690          instrument_expression(p, f);          output_expression(p, f, false);
691        }        }
692      }      }
693      if (node->pn_extra == PNX_ENDCOMMA) {      if (node->pn_extra == PNX_ENDCOMMA) {
# Line 621  Line 696 
696      Stream_write_char(f, ']');      Stream_write_char(f, ']');
697      break;      break;
698    case TOK_RC:    case TOK_RC:
699        if (parenthesize_object_literals) {
700          Stream_write_char(f, '(');
701        }
702      Stream_write_char(f, '{');      Stream_write_char(f, '{');
703      for (struct JSParseNode * p = node->pn_head; p != NULL; p = p->pn_next) {      for (struct JSParseNode * p = node->pn_head; p != NULL; p = p->pn_next) {
704        assert(p->pn_type == TOK_COLON);        if (p->pn_type != TOK_COLON) {
705            fatal_source(file_id, p->pn_pos.begin.lineno, "unsupported node type (%d)", p->pn_type);
706          }
707        if (p != node->pn_head) {        if (p != node->pn_head) {
708          Stream_write_string(f, ", ");          Stream_write_string(f, ", ");
709        }        }
# Line 638  Line 718 
718          else {          else {
719            Stream_write_string(f, "set ");            Stream_write_string(f, "set ");
720          }          }
721          instrument_expression(p->pn_left, f);          output_expression(p->pn_left, f, false);
722          if (p->pn_right->pn_type != TOK_FUNCTION) {          if (p->pn_right->pn_type != TOK_FUNCTION) {
723            fatal("parse error: expected function");            fatal_source(file_id, p->pn_pos.begin.lineno, "expected function");
724          }          }
725          instrument_function(p->pn_right, f, 0, FUNCTION_GETTER_OR_SETTER);          instrument_function(p->pn_right, f, 0, FUNCTION_GETTER_OR_SETTER);
726          break;          break;
727        default:        default:
728          instrument_expression(p->pn_left, f);          output_expression(p->pn_left, f, false);
729          Stream_write_string(f, ": ");          Stream_write_string(f, ": ");
730          instrument_expression(p->pn_right, f);          output_expression(p->pn_right, f, false);
731          break;          break;
732        }        }
733      }      }
734      Stream_write_char(f, '}');      Stream_write_char(f, '}');
735        if (parenthesize_object_literals) {
736          Stream_write_char(f, ')');
737        }
738      break;      break;
739    case TOK_RP:    case TOK_RP:
740      Stream_write_char(f, '(');      Stream_write_char(f, '(');
741      instrument_expression(node->pn_kid, f);      output_expression(node->pn_kid, f, false);
742      Stream_write_char(f, ')');      Stream_write_char(f, ')');
743      break;      break;
744    case TOK_NAME:    case TOK_NAME:
745      print_string_atom(node->pn_atom, f);      print_string_atom(node->pn_atom, f);
746        if (node->pn_expr != NULL) {
747          Stream_write_string(f, " = ");
748          output_expression(node->pn_expr, f, false);
749        }
750      break;      break;
751    case TOK_STRING:    case TOK_STRING:
752      print_quoted_string_atom(node->pn_atom, f);      print_quoted_string_atom(node->pn_atom, f);
# Line 707  Line 794 
794      }      }
795      break;      break;
796    case TOK_INSTANCEOF:    case TOK_INSTANCEOF:
797      instrument_expression(node->pn_left, f);      output_expression(node->pn_left, f, parenthesize_object_literals);
798      Stream_write_string(f, " instanceof ");      Stream_write_string(f, " instanceof ");
799      instrument_expression(node->pn_right, f);      output_expression(node->pn_right, f, false);
800      break;      break;
801    case TOK_IN:    case TOK_IN:
802      instrument_expression(node->pn_left, f);      output_expression(node->pn_left, f, false);
803      Stream_write_string(f, " in ");      Stream_write_string(f, " in ");
804      instrument_expression(node->pn_right, f);      output_expression(node->pn_right, f, false);
805      break;      break;
806    case TOK_LEXICALSCOPE:    case TOK_LEXICALSCOPE:
807      assert(node->pn_arity == PN_NAME);      assert(node->pn_arity == PN_NAME);
# Line 725  Line 812 
812      assert(node->pn_expr->pn_left->pn_arity == PN_LIST);      assert(node->pn_expr->pn_left->pn_arity == PN_LIST);
813      instrument_declarations(node->pn_expr->pn_left, f);      instrument_declarations(node->pn_expr->pn_left, f);
814      Stream_write_string(f, ") ");      Stream_write_string(f, ") ");
815      instrument_expression(node->pn_expr->pn_right, f);      output_expression(node->pn_expr->pn_right, f, true);
816      break;      break;
817    case TOK_YIELD:    case TOK_YIELD:
818      assert(node->pn_arity == PN_UNARY);      assert(node->pn_arity == PN_UNARY);
819      Stream_write_string(f, "yield ");      Stream_write_string(f, "yield");
820      instrument_expression(node->pn_kid, f);      if (node->pn_kid != NULL) {
821          Stream_write_char(f, ' ');
822          output_expression(node->pn_kid, f, true);
823        }
824      break;      break;
825    case TOK_ARRAYCOMP:    case TOK_ARRAYCOMP:
826      assert(node->pn_arity == PN_LIST);      assert(node->pn_arity == PN_LIST);
# Line 747  Line 837 
837          abort();          abort();
838          break;          break;
839        }        }
       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;  
       }  
840        Stream_write_char(f, '[');        Stream_write_char(f, '[');
841        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, ')');  
       }  
842        Stream_write_char(f, ']');        Stream_write_char(f, ']');
843      }      }
844      break;      break;
# Line 791  Line 853 
853      instrument_declarations(node, f);      instrument_declarations(node, f);
854      break;      break;
855    default:    default:
856      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);
857    }    }
858  }  }
859    
# Line 820  Line 882 
882      uint16_t line = node->pn_pos.begin.lineno;      uint16_t line = node->pn_pos.begin.lineno;
883      if (! is_jscoverage_if) {      if (! is_jscoverage_if) {
884        if (line > num_lines) {        if (line > num_lines) {
885          fatal("%s: script contains more than 65,535 lines", file_id);          fatal("file %s contains more than 65,535 lines", file_id);
886        }        }
887        if (line >= 2 && exclusive_directives[line - 2]) {        if (line >= 2 && exclusive_directives[line - 2]) {
888          is_jscoverage_if = true;          is_jscoverage_if = true;
# Line 829  Line 891 
891    
892      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
893      Stream_write_string(f, "if (");      Stream_write_string(f, "if (");
894      instrument_expression(node->pn_kid1, f);      output_expression(node->pn_kid1, f, false);
895      Stream_write_string(f, ") {\n");      Stream_write_string(f, ") {\n");
896      if (is_jscoverage_if && node->pn_kid3) {      if (is_jscoverage_if && node->pn_kid3) {
897        uint16_t else_start = node->pn_kid3->pn_pos.begin.lineno;        uint16_t else_start = node->pn_kid3->pn_pos.begin.lineno;
# Line 866  Line 928 
928      assert(node->pn_arity == PN_BINARY);      assert(node->pn_arity == PN_BINARY);
929      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
930      Stream_write_string(f, "switch (");      Stream_write_string(f, "switch (");
931      instrument_expression(node->pn_left, f);      output_expression(node->pn_left, f, false);
932      Stream_write_string(f, ") {\n");      Stream_write_string(f, ") {\n");
933      for (struct JSParseNode * p = node->pn_right->pn_head; p != NULL; p = p->pn_next) {      {
934        Stream_printf(f, "%*s", indent, "");        JSParseNode * list = node->pn_right;
935        switch (p->pn_type) {        if (list->pn_type == TOK_LEXICALSCOPE) {
936        case TOK_CASE:          list = list->pn_expr;
937          Stream_write_string(f, "case ");        }
938          instrument_expression(p->pn_left, f);        for (struct JSParseNode * p = list->pn_head; p != NULL; p = p->pn_next) {
939          Stream_write_string(f, ":\n");          Stream_printf(f, "%*s", indent, "");
940          break;          switch (p->pn_type) {
941        case TOK_DEFAULT:          case TOK_CASE:
942          Stream_write_string(f, "default:\n");            Stream_write_string(f, "case ");
943          break;            output_expression(p->pn_left, f, false);
944        default:            Stream_write_string(f, ":\n");
945          abort();            break;
946          break;          case TOK_DEFAULT:
947              Stream_write_string(f, "default:\n");
948              break;
949            default:
950              abort();
951              break;
952            }
953            instrument_statement(p->pn_right, f, indent + 2, false);
954        }        }
       instrument_statement(p->pn_right, f, indent + 2, false);  
955      }      }
956      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
957      Stream_write_string(f, "}\n");      Stream_write_string(f, "}\n");
# Line 896  Line 964 
964      assert(node->pn_arity == PN_BINARY);      assert(node->pn_arity == PN_BINARY);
965      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
966      Stream_write_string(f, "while (");      Stream_write_string(f, "while (");
967      instrument_expression(node->pn_left, f);      output_expression(node->pn_left, f, false);
968      Stream_write_string(f, ") {\n");      Stream_write_string(f, ") {\n");
969      instrument_statement(node->pn_right, f, indent + 2, false);      instrument_statement(node->pn_right, f, indent + 2, false);
970      Stream_write_string(f, "}\n");      Stream_write_string(f, "}\n");
# Line 909  Line 977 
977      Stream_write_string(f, "}\n");      Stream_write_string(f, "}\n");
978      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
979      Stream_write_string(f, "while (");      Stream_write_string(f, "while (");
980      instrument_expression(node->pn_right, f);      output_expression(node->pn_right, f, false);
981      Stream_write_string(f, ");\n");      Stream_write_string(f, ");\n");
982      break;      break;
983    case TOK_FOR:    case TOK_FOR:
# Line 926  Line 994 
994        assert(node->pn_left->pn_arity == PN_TERNARY);        assert(node->pn_left->pn_arity == PN_TERNARY);
995        Stream_write_string(f, "for (");        Stream_write_string(f, "for (");
996        if (node->pn_left->pn_kid1) {        if (node->pn_left->pn_kid1) {
997          instrument_expression(node->pn_left->pn_kid1, f);          output_expression(node->pn_left->pn_kid1, f, false);
998        }        }
999        Stream_write_string(f, ";");        Stream_write_string(f, ";");
1000        if (node->pn_left->pn_kid2) {        if (node->pn_left->pn_kid2) {
1001          Stream_write_char(f, ' ');          Stream_write_char(f, ' ');
1002          instrument_expression(node->pn_left->pn_kid2, f);          output_expression(node->pn_left->pn_kid2, f, false);
1003        }        }
1004        Stream_write_string(f, ";");        Stream_write_string(f, ";");
1005        if (node->pn_left->pn_kid3) {        if (node->pn_left->pn_kid3) {
1006          Stream_write_char(f, ' ');          Stream_write_char(f, ' ');
1007          instrument_expression(node->pn_left->pn_kid3, f);          output_expression(node->pn_left->pn_kid3, f, false);
1008        }        }
1009        Stream_write_char(f, ')');        Stream_write_char(f, ')');
1010        break;        break;
# Line 952  Line 1020 
1020      assert(node->pn_arity == PN_UNARY);      assert(node->pn_arity == PN_UNARY);
1021      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
1022      Stream_write_string(f, "throw ");      Stream_write_string(f, "throw ");
1023      instrument_expression(node->pn_u.unary.kid, f);      output_expression(node->pn_u.unary.kid, f, false);
1024      Stream_write_string(f, ";\n");      Stream_write_string(f, ";\n");
1025      break;      break;
1026    case TOK_TRY:    case TOK_TRY:
# Line 969  Line 1037 
1037          assert(catch->pn_type == TOK_CATCH);          assert(catch->pn_type == TOK_CATCH);
1038          Stream_printf(f, "%*s", indent, "");          Stream_printf(f, "%*s", indent, "");
1039          Stream_write_string(f, "catch (");          Stream_write_string(f, "catch (");
1040          /* 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);  
1041          if (catch->pn_kid2) {          if (catch->pn_kid2) {
1042            Stream_write_string(f, " if ");            Stream_write_string(f, " if ");
1043            instrument_expression(catch->pn_kid2, f);            output_expression(catch->pn_kid2, f, false);
1044          }          }
1045          Stream_write_string(f, ") {\n");          Stream_write_string(f, ") {\n");
1046          instrument_statement(catch->pn_kid3, f, indent + 2, false);          instrument_statement(catch->pn_kid3, f, indent + 2, false);
# Line 1012  Line 1075 
1075      assert(node->pn_arity == PN_BINARY);      assert(node->pn_arity == PN_BINARY);
1076      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
1077      Stream_write_string(f, "with (");      Stream_write_string(f, "with (");
1078      instrument_expression(node->pn_left, f);      output_expression(node->pn_left, f, false);
1079      Stream_write_string(f, ") {\n");      Stream_write_string(f, ") {\n");
1080      instrument_statement(node->pn_right, f, indent + 2, false);      instrument_statement(node->pn_right, f, indent + 2, false);
1081      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
# Line 1020  Line 1083 
1083      break;      break;
1084    case TOK_VAR:    case TOK_VAR:
1085      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
1086      instrument_expression(node, f);      output_expression(node, f, false);
1087      Stream_write_string(f, ";\n");      Stream_write_string(f, ";\n");
1088      break;      break;
1089    case TOK_RETURN:    case TOK_RETURN:
# Line 1029  Line 1092 
1092      Stream_write_string(f, "return");      Stream_write_string(f, "return");
1093      if (node->pn_kid != NULL) {      if (node->pn_kid != NULL) {
1094        Stream_write_char(f, ' ');        Stream_write_char(f, ' ');
1095        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, true);
1096      }      }
1097      Stream_write_string(f, ";\n");      Stream_write_string(f, ";\n");
1098      break;      break;
# Line 1037  Line 1100 
1100      assert(node->pn_arity == PN_UNARY);      assert(node->pn_arity == PN_UNARY);
1101      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
1102      if (node->pn_kid != NULL) {      if (node->pn_kid != NULL) {
1103        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, true);
1104      }      }
1105      Stream_write_string(f, ";\n");      Stream_write_string(f, ";\n");
1106      break;      break;
1107    case TOK_COLON:    case TOK_COLON:
1108      {
1109      assert(node->pn_arity == PN_NAME);      assert(node->pn_arity == PN_NAME);
     /*  
     This one is tricky: can't output instrumentation between the label and the  
     statement it's supposed to label ...  
     */  
1110      Stream_printf(f, "%*s", indent < 2? 0: indent - 2, "");      Stream_printf(f, "%*s", indent < 2? 0: indent - 2, "");
1111      print_string_atom(node->pn_atom, f);      print_string_atom(node->pn_atom, f);
1112      Stream_write_string(f, ":\n");      Stream_write_string(f, ":\n");
1113      /*      JSParseNode * labelled = node->pn_expr;
1114      ... use output_statement instead of instrument_statement.      if (labelled->pn_type == TOK_LEXICALSCOPE) {
1115      */        labelled = labelled->pn_expr;
1116      output_statement(node->pn_expr, f, indent, false);      }
1117        if (labelled->pn_type == TOK_LC) {
1118          /* labelled block */
1119          Stream_printf(f, "%*s", indent, "");
1120          Stream_write_string(f, "{\n");
1121          instrument_statement(labelled, f, indent + 2, false);
1122          Stream_printf(f, "%*s", indent, "");
1123          Stream_write_string(f, "}\n");
1124        }
1125        else {
1126          /*
1127          This one is tricky: can't output instrumentation between the label and the
1128          statement it's supposed to label, so use output_statement instead of
1129          instrument_statement.
1130          */
1131          output_statement(labelled, f, indent, false);
1132        }
1133      break;      break;
1134      }
1135    case TOK_LEXICALSCOPE:    case TOK_LEXICALSCOPE:
1136      /* let statement */      /* let statement */
1137      assert(node->pn_arity == PN_NAME);      assert(node->pn_arity == PN_NAME);
# Line 1097  Line 1174 
1174      case PN_LIST:      case PN_LIST:
1175        /* let definition */        /* let definition */
1176        Stream_printf(f, "%*s", indent, "");        Stream_printf(f, "%*s", indent, "");
1177        instrument_expression(node, f);        output_expression(node, f, false);
1178        Stream_write_string(f, ";\n");        Stream_write_string(f, ";\n");
1179        break;        break;
1180      default:      default:
# Line 1105  Line 1182 
1182        break;        break;
1183      }      }
1184      break;      break;
1185      case TOK_DEBUGGER:
1186        Stream_printf(f, "%*s", indent, "");
1187        Stream_write_string(f, "debugger;\n");
1188        break;
1189    default:    default:
1190      fatal("unsupported node type in file %s: %d", file_id, node->pn_type);      fatal_source(file_id, node->pn_pos.begin.lineno, "unsupported node type (%d)", node->pn_type);
1191    }    }
1192  }  }
1193    
# Line 1119  Line 1200 
1200    if (node->pn_type != TOK_LC && node->pn_type != TOK_LEXICALSCOPE) {    if (node->pn_type != TOK_LC && node->pn_type != TOK_LEXICALSCOPE) {
1201      uint16_t line = node->pn_pos.begin.lineno;      uint16_t line = node->pn_pos.begin.lineno;
1202      if (line > num_lines) {      if (line > num_lines) {
1203        fatal("%s: script contains more than 65,535 lines", file_id);        fatal("file %s contains more than 65,535 lines", file_id);
1204      }      }
1205    
1206      /* the root node has line number 0 */      /* the root node has line number 0 */
# Line 1167  Line 1248 
1248  }  }
1249    
1250  static void error_reporter(JSContext * context, const char * message, JSErrorReport * report) {  static void error_reporter(JSContext * context, const char * message, JSErrorReport * report) {
1251    fprintf(stderr, "jscoverage: parse error: line %u: %s\n", report->lineno, message);    warn_source(file_id, report->lineno, "%s", message);
1252  }  }
1253    
1254  void jscoverage_instrument_js(const char * id, const uint16_t * characters, size_t num_characters, Stream * output) {  void jscoverage_instrument_js(const char * id, const uint16_t * characters, size_t num_characters, Stream * output) {
# Line 1176  Line 1257 
1257    /* parse the javascript */    /* parse the javascript */
1258    JSParseContext parse_context;    JSParseContext parse_context;
1259    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)) {
1260      fatal("cannot create token stream from file: %s", file_id);      fatal("cannot create token stream from file %s", file_id);
1261    }    }
1262    JSErrorReporter old_error_reporter = JS_SetErrorReporter(context, error_reporter);    JSErrorReporter old_error_reporter = JS_SetErrorReporter(context, error_reporter);
1263    JSParseNode * node = js_ParseScript(context, global, &parse_context);    JSParseNode * node = js_ParseScript(context, global, &parse_context);
1264    if (node == NULL) {    if (node == NULL) {
1265      js_ReportUncaughtException(context);      js_ReportUncaughtException(context);
1266      fatal("parse error in file: %s", file_id);      fatal("parse error in file %s", file_id);
1267    }    }
1268    JS_SetErrorReporter(context, old_error_reporter);    JS_SetErrorReporter(context, old_error_reporter);
1269    num_lines = node->pn_pos.end.lineno;    num_lines = node->pn_pos.end.lineno;
# Line 1203  Line 1284 
1284    size_t i = 0;    size_t i = 0;
1285    while (i < num_characters) {    while (i < num_characters) {
1286      if (line_number == UINT16_MAX) {      if (line_number == UINT16_MAX) {
1287        fatal("%s: script has more than 65,535 lines", file_id);        fatal("file %s contains more than 65,535 lines", file_id);
1288      }      }
1289      line_number++;      line_number++;
1290      size_t line_start = i;      size_t line_start = i;
# Line 1270  Line 1351 
1351    
1352    /* write line number info to the output */    /* write line number info to the output */
1353    Stream_write_string(output, "/* automatically generated by JSCoverage - do not edit */\n");    Stream_write_string(output, "/* automatically generated by JSCoverage - do not edit */\n");
1354    Stream_write_string(output, "if (! top._$jscoverage) {\n  top._$jscoverage = {};\n}\n");    if (jscoverage_mozilla) {
1355    Stream_write_string(output, "var _$jscoverage = top._$jscoverage;\n");      Stream_write_string(output, "try {\n");
1356        Stream_write_string(output, "  Components.utils.import('resource://gre/modules/jscoverage.jsm');\n");
1357        Stream_printf(output, "  dump('%s: successfully imported jscoverage module\\n');\n", id);
1358        Stream_write_string(output, "}\n");
1359        Stream_write_string(output, "catch (e) {\n");
1360        Stream_write_string(output, "  _$jscoverage = {};\n");
1361        Stream_printf(output, "  dump('%s: failed to import jscoverage module - coverage not available for this file\\n');\n", id);
1362        Stream_write_string(output, "}\n");
1363      }
1364      else {
1365        Stream_write_string(output, "if (! top._$jscoverage) {\n  top._$jscoverage = {};\n}\n");
1366        Stream_write_string(output, "var _$jscoverage = top._$jscoverage;\n");
1367      }
1368    Stream_printf(output, "if (! _$jscoverage['%s']) {\n", file_id);    Stream_printf(output, "if (! _$jscoverage['%s']) {\n", file_id);
1369    Stream_printf(output, "  _$jscoverage['%s'] = [];\n", file_id);    Stream_printf(output, "  _$jscoverage['%s'] = [];\n", file_id);
1370    for (int i = 0; i < num_lines; i++) {    for (int i = 0; i < num_lines; i++) {

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

  ViewVC Help
Powered by ViewVC 1.1.24