/[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 293 by siliconforks, Sun Oct 12 16:41:57 2008 UTC revision 379 by siliconforks, Tue Oct 28 17:50:42 2008 UTC
# Line 26  Line 26 
26  #include <string.h>  #include <string.h>
27    
28  #include <jsapi.h>  #include <jsapi.h>
29    #include <jsarena.h>
30  #include <jsatom.h>  #include <jsatom.h>
31    #include <jsemit.h>
32  #include <jsexn.h>  #include <jsexn.h>
33  #include <jsfun.h>  #include <jsfun.h>
34  #include <jsinterp.h>  #include <jsinterp.h>
35    #include <jsiter.h>
36  #include <jsparse.h>  #include <jsparse.h>
37  #include <jsregexp.h>  #include <jsregexp.h>
38  #include <jsscope.h>  #include <jsscope.h>
# Line 49  Line 52 
52    struct IfDirective * next;    struct IfDirective * next;
53  };  };
54    
55    bool jscoverage_mozilla = false;
56    
57  static bool * exclusive_directives = NULL;  static bool * exclusive_directives = NULL;
58    
59  static JSRuntime * runtime = NULL;  static JSRuntime * runtime = NULL;
60  static JSContext * context = NULL;  static JSContext * context = NULL;
61  static JSObject * global = NULL;  static JSObject * global = NULL;
62    static JSVersion js_version = JSVERSION_ECMA_3;
63    
64  /*  /*
65  JSParseNode objects store line numbers starting from 1.  JSParseNode objects store line numbers starting from 1.
# Line 63  Line 69 
69  static char * lines = NULL;  static char * lines = NULL;
70  static uint16_t num_lines = 0;  static uint16_t num_lines = 0;
71    
72    void jscoverage_set_js_version(const char * version) {
73      js_version = JS_StringToVersion(version);
74      if (js_version != JSVERSION_UNKNOWN) {
75        return;
76      }
77    
78      char * end;
79      js_version = (JSVersion) strtol(version, &end, 10);
80      if ((size_t) (end - version) != strlen(version)) {
81        fatal("invalid version: %s", version);
82      }
83    }
84    
85  void jscoverage_init(void) {  void jscoverage_init(void) {
86    runtime = JS_NewRuntime(8L * 1024L * 1024L);    runtime = JS_NewRuntime(8L * 1024L * 1024L);
87    if (runtime == NULL) {    if (runtime == NULL) {
# Line 74  Line 93 
93      fatal("cannot create context");      fatal("cannot create context");
94    }    }
95    
96      JS_SetVersion(context, js_version);
97    
98    global = JS_NewObject(context, NULL, NULL, NULL);    global = JS_NewObject(context, NULL, NULL, NULL);
99    if (global == NULL) {    if (global == NULL) {
100      fatal("cannot create global object");      fatal("cannot create global object");
# Line 147  Line 168 
168        case 0xa:        case 0xa:
169          Stream_write_string(f, "\\n");          Stream_write_string(f, "\\n");
170          break;          break;
171          /* IE doesn't support this */
172          /*
173        case 0xb:        case 0xb:
174          Stream_write_string(f, "\\v");          Stream_write_string(f, "\\v");
175          break;          break;
176          */
177        case 0xc:        case 0xc:
178          Stream_write_string(f, "\\f");          Stream_write_string(f, "\\f");
179          break;          break;
# Line 196  Line 220 
220    
221  static const char * get_op(uint8 op) {  static const char * get_op(uint8 op) {
222    switch(op) {    switch(op) {
223      case JSOP_OR:
224        return "||";
225      case JSOP_AND:
226        return "&&";
227    case JSOP_BITOR:    case JSOP_BITOR:
228      return "|";      return "|";
229    case JSOP_BITXOR:    case JSOP_BITXOR:
# Line 206  Line 234 
234      return "==";      return "==";
235    case JSOP_NE:    case JSOP_NE:
236      return "!=";      return "!=";
237    case JSOP_NEW_EQ:    case JSOP_STRICTEQ:
238      return "===";      return "===";
239    case JSOP_NEW_NE:    case JSOP_STRICTNE:
240      return "!==";      return "!==";
241    case JSOP_LT:    case JSOP_LT:
242      return "<";      return "<";
# Line 239  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);
273    
274  enum FunctionType {  enum FunctionType {
275    FUNCTION_NORMAL,    FUNCTION_NORMAL,
276    FUNCTION_GETTER_OR_SETTER    FUNCTION_GETTER_OR_SETTER
277  };  };
278    
279    static void output_for_in(JSParseNode * node, Stream * f) {
280      assert(node->pn_type == TOK_FOR);
281      assert(node->pn_arity == PN_BINARY);
282      Stream_write_string(f, "for ");
283      if (node->pn_iflags & JSITER_FOREACH) {
284        Stream_write_string(f, "each ");
285      }
286      Stream_write_char(f, '(');
287      output_expression(node->pn_left, f, false);
288      Stream_write_char(f, ')');
289    }
290    
291    static void output_array_comprehension_or_generator_expression(JSParseNode * node, Stream * f) {
292      assert(node->pn_type == TOK_LEXICALSCOPE);
293      assert(node->pn_arity == PN_NAME);
294      JSParseNode * for_node = node->pn_expr;
295      assert(for_node->pn_type == TOK_FOR);
296      assert(for_node->pn_arity == PN_BINARY);
297      JSParseNode * p = for_node;
298      while (p->pn_type == TOK_FOR) {
299        p = p->pn_right;
300      }
301      JSParseNode * if_node = NULL;
302      if (p->pn_type == TOK_IF) {
303        if_node = p;
304        assert(if_node->pn_arity == PN_TERNARY);
305        p = if_node->pn_kid2;
306      }
307      assert(p->pn_arity == PN_UNARY);
308      p = p->pn_kid;
309      if (p->pn_type == TOK_YIELD) {
310        /* for generator expressions */
311        p = p->pn_kid;
312      }
313    
314      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);
330    assert(node->pn_arity == PN_FUNC);    assert(node->pn_arity == PN_FUNC);
331    assert(ATOM_IS_OBJECT(node->pn_funAtom));    JSObject * object = node->pn_funpob->object;
   JSObject * object = ATOM_TO_OBJECT(node->pn_funAtom);  
332    assert(JS_ObjectIsFunction(context, object));    assert(JS_ObjectIsFunction(context, object));
333    JSFunction * function = (JSFunction *) JS_GetPrivate(context, object);    JSFunction * function = (JSFunction *) JS_GetPrivate(context, object);
334    assert(function);    assert(function);
335    assert(object == function->object);    assert(object == &function->object);
336    Stream_printf(f, "%*s", indent, "");    Stream_printf(f, "%*s", indent, "");
337    if (type == FUNCTION_NORMAL) {    if (type == FUNCTION_NORMAL) {
338      Stream_write_string(f, "function");      Stream_write_string(f, "function ");
339    }    }
340    
341    /* function name */    /* function name */
342    if (function->atom) {    if (function->atom) {
     Stream_write_char(f, ' ');  
343      print_string_atom(function->atom, f);      print_string_atom(function->atom, f);
344    }    }
345    
346    /* function parameters */    /*
347    Stream_write_string(f, "(");    function parameters - see JS_DecompileFunction in jsapi.cpp, which calls
348    JSAtom ** params = xnew(JSAtom *, function->nargs);    js_DecompileFunction in jsopcode.cpp
349    for (int i = 0; i < function->nargs; i++) {    */
350      /* initialize to NULL for sanity check */    Stream_write_char(f, '(');
351      params[i] = NULL;    JSArenaPool pool;
352    }    JS_INIT_ARENA_POOL(&pool, "instrument_function", 256, 1, &context->scriptStackQuota);
353    JSScope * scope = OBJ_SCOPE(object);    jsuword * local_names = NULL;
354    for (JSScopeProperty * scope_property = SCOPE_LAST_PROP(scope); scope_property != NULL; scope_property = scope_property->parent) {    if (JS_GET_LOCAL_NAME_COUNT(function)) {
355      if (scope_property->getter != js_GetArgument) {      local_names = js_GetLocalNameArray(context, function, &pool);
356        continue;      if (local_names == NULL) {
357          fatal("out of memory");
358      }      }
     assert(scope_property->flags & SPROP_HAS_SHORTID);  
     assert((uint16) scope_property->shortid < function->nargs);  
     assert(JSID_IS_ATOM(scope_property->id));  
     params[(uint16) scope_property->shortid] = JSID_TO_ATOM(scope_property->id);  
359    }    }
360      bool destructuring = false;
361    for (int i = 0; i < function->nargs; i++) {    for (int i = 0; i < function->nargs; i++) {
     assert(params[i] != NULL);  
362      if (i > 0) {      if (i > 0) {
363        Stream_write_string(f, ", ");        Stream_write_string(f, ", ");
364      }      }
365      if (ATOM_IS_STRING(params[i])) {      JSAtom * param = JS_LOCAL_NAME_TO_ATOM(local_names[i]);
366        print_string_atom(params[i], f);      if (param == NULL) {
367          destructuring = true;
368          JSParseNode * expression = NULL;
369          assert(node->pn_body->pn_type == TOK_LC || node->pn_body->pn_type == TOK_BODY);
370          JSParseNode * semi = node->pn_body->pn_head;
371          assert(semi->pn_type == TOK_SEMI);
372          JSParseNode * comma = semi->pn_kid;
373          assert(comma->pn_type == TOK_COMMA);
374          for (JSParseNode * p = comma->pn_head; p != NULL; p = p->pn_next) {
375            assert(p->pn_type == TOK_ASSIGN);
376            JSParseNode * rhs = p->pn_right;
377            assert(JSSTRING_LENGTH(ATOM_TO_STRING(rhs->pn_atom)) == 0);
378            if (rhs->pn_slot == i) {
379              expression = p->pn_left;
380              break;
381            }
382          }
383          assert(expression != NULL);
384          output_expression(expression, f, false);
385        }
386        else {
387          print_string_atom(param, f);
388      }      }
389    }    }
390      JS_FinishArenaPool(&pool);
391    Stream_write_string(f, ") {\n");    Stream_write_string(f, ") {\n");
   free(params);  
392    
393    /* function body */    /* function body */
394    instrument_statement(node->pn_body, f, indent + 2, false);    if (function->flags & JSFUN_EXPR_CLOSURE) {
395        /* expression closure - use output_statement instead of instrument_statement */
396        if (node->pn_body->pn_type == TOK_BODY) {
397          assert(node->pn_body->pn_arity == PN_LIST);
398          assert(node->pn_body->pn_count == 2);
399          output_statement(node->pn_body->pn_head->pn_next, f, indent + 2, false);
400        }
401        else {
402          output_statement(node->pn_body, f, indent + 2, false);
403        }
404      }
405      else {
406        assert(node->pn_body->pn_type == TOK_LC);
407        assert(node->pn_body->pn_arity == PN_LIST);
408        JSParseNode * p = node->pn_body->pn_head;
409        if (destructuring) {
410          p = p->pn_next;
411        }
412        for (; p != NULL; p = p->pn_next) {
413          instrument_statement(p, f, indent + 2, false);
414        }
415      }
416    
417    Stream_write_string(f, "}\n");    Stream_write_string(f, "}\n");
418  }  }
419    
420  static void instrument_function_call(JSParseNode * node, Stream * f) {  static void instrument_function_call(JSParseNode * node, Stream * f) {
421    instrument_expression(node->pn_head, f);    JSParseNode * function_node = node->pn_head;
422      if (function_node->pn_type == TOK_FUNCTION) {
423        JSObject * object = function_node->pn_funpob->object;
424        assert(JS_ObjectIsFunction(context, object));
425        JSFunction * function = (JSFunction *) JS_GetPrivate(context, object);
426        assert(function);
427        assert(object == &function->object);
428    
429        if (function_node->pn_flags & TCF_GENEXP_LAMBDA) {
430          /* it's a generator expression */
431          Stream_write_char(f, '(');
432          output_array_comprehension_or_generator_expression(function_node->pn_body, f);
433          Stream_write_char(f, ')');
434          return;
435        }
436        else {
437          Stream_write_char(f, '(');
438          output_expression(function_node, f, false);
439          Stream_write_char(f, ')');
440        }
441      }
442      else {
443        output_expression(function_node, f, false);
444      }
445    Stream_write_char(f, '(');    Stream_write_char(f, '(');
446    for (struct JSParseNode * p = node->pn_head->pn_next; p != NULL; p = p->pn_next) {    for (struct JSParseNode * p = function_node->pn_next; p != NULL; p = p->pn_next) {
447      if (p != node->pn_head->pn_next) {      if (p != node->pn_head->pn_next) {
448        Stream_write_string(f, ", ");        Stream_write_string(f, ", ");
449      }      }
450      instrument_expression(p, f);      output_expression(p, f, false);
451    }    }
452    Stream_write_char(f, ')');    Stream_write_char(f, ')');
453  }  }
454    
455    static void instrument_declarations(JSParseNode * list, Stream * f) {
456      assert(list->pn_arity == PN_LIST);
457      for (JSParseNode * p = list->pn_head; p != NULL; p = p->pn_next) {
458        if (p != list->pn_head) {
459          Stream_write_string(f, ", ");
460        }
461        output_expression(p, f, false);
462      }
463    }
464    
465  /*  /*
466  See <Expressions> in jsparse.h.  See <Expressions> in jsparse.h.
467  TOK_FUNCTION is handled as a statement and as an expression.  TOK_FUNCTION is handled as a statement and as an expression.
# Line 326  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 336  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 361  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:
     instrument_expression(node->pn_left, f);  
     Stream_write_string(f, " || ");  
     instrument_expression(node->pn_right, f);  
     break;  
523    case TOK_AND:    case TOK_AND:
     instrument_expression(node->pn_left, f);  
     Stream_write_string(f, " && ");  
     instrument_expression(node->pn_right, f);  
     break;  
524    case TOK_BITOR:    case TOK_BITOR:
525    case TOK_BITXOR:    case TOK_BITXOR:
526    case TOK_BITAND:    case TOK_BITAND:
# Line 392  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 412  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 449  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 480  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        /* XXX - semantics changed in 1.7 */        bool must_quote;
650        if (! ATOM_KEYWORD(node->pn_atom) && 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 521  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 530  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 547  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);
753      break;      break;
754    case TOK_OBJECT:    case TOK_REGEXP:
755      switch (node->pn_op) {      assert(node->pn_op == JSOP_REGEXP);
756      case JSOP_OBJECT:      {
757        /* I assume this is JSOP_REGEXP */        JSObject * object = node->pn_pob->object;
758        abort();        jsval result;
759        break;        js_regexp_toString(context, object, &result);
760      case JSOP_REGEXP:        print_regex(result, f);
       assert(ATOM_IS_OBJECT(node->pn_atom));  
       {  
         JSObject * object = ATOM_TO_OBJECT(node->pn_atom);  
         jsval result;  
         js_regexp_toString(context, object, 0, NULL, &result);  
         print_regex(result, f);  
       }  
       break;  
     default:  
       abort();  
       break;  
761      }      }
762      break;      break;
763    case TOK_NUMBER:    case TOK_NUMBER:
# Line 627  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    default:    case TOK_LEXICALSCOPE:
807      fatal("unsupported node type in file %s: %d", file_id, node->pn_type);      assert(node->pn_arity == PN_NAME);
808    }      assert(node->pn_expr->pn_type == TOK_LET);
809  }      assert(node->pn_expr->pn_arity == PN_BINARY);
810        Stream_write_string(f, "let(");
811  static void instrument_var_statement(JSParseNode * node, Stream * f, int indent) {      assert(node->pn_expr->pn_left->pn_type == TOK_LP);
812    assert(node->pn_arity == PN_LIST);      assert(node->pn_expr->pn_left->pn_arity == PN_LIST);
813    Stream_printf(f, "%*s", indent, "");      instrument_declarations(node->pn_expr->pn_left, f);
814    Stream_write_string(f, "var ");      Stream_write_string(f, ") ");
815    for (struct JSParseNode * p = node->pn_u.list.head; p != NULL; p = p->pn_next) {      output_expression(node->pn_expr->pn_right, f, true);
816      assert(p->pn_type == TOK_NAME);      break;
817      assert(p->pn_arity == PN_NAME);    case TOK_YIELD:
818      if (p != node->pn_head) {      assert(node->pn_arity == PN_UNARY);
819        Stream_write_string(f, ", ");      Stream_write_string(f, "yield");
820        if (node->pn_kid != NULL) {
821          Stream_write_char(f, ' ');
822          output_expression(node->pn_kid, f, true);
823      }      }
824      print_string_atom(p->pn_atom, f);      break;
825      if (p->pn_expr != NULL) {    case TOK_ARRAYCOMP:
826        Stream_write_string(f, " = ");      assert(node->pn_arity == PN_LIST);
827        instrument_expression(p->pn_expr, f);      {
828          JSParseNode * block_node;
829          switch (node->pn_count) {
830          case 1:
831            block_node = node->pn_head;
832            break;
833          case 2:
834            block_node = node->pn_head->pn_next;
835            break;
836          default:
837            abort();
838            break;
839          }
840          Stream_write_char(f, '[');
841          output_array_comprehension_or_generator_expression(block_node, f);
842          Stream_write_char(f, ']');
843      }      }
844        break;
845      case TOK_VAR:
846        assert(node->pn_arity == PN_LIST);
847        Stream_write_string(f, "var ");
848        instrument_declarations(node, f);
849        break;
850      case TOK_LET:
851        assert(node->pn_arity == PN_LIST);
852        Stream_write_string(f, "let ");
853        instrument_declarations(node, f);
854        break;
855      default:
856        fatal_source(file_id, node->pn_pos.begin.lineno, "unsupported node type (%d)", node->pn_type);
857    }    }
858  }  }
859    
# Line 684  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 693  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 730  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 760  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 773  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:
984      assert(node->pn_arity == PN_BINARY);      assert(node->pn_arity == PN_BINARY);
985      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
     Stream_write_string(f, "for (");  
986      switch (node->pn_left->pn_type) {      switch (node->pn_left->pn_type) {
987      case TOK_IN:      case TOK_IN:
988        /* for/in */        /* for/in */
989        assert(node->pn_left->pn_arity == PN_BINARY);        assert(node->pn_left->pn_arity == PN_BINARY);
990        switch (node->pn_left->pn_left->pn_type) {        output_for_in(node, f);
       case TOK_VAR:  
         instrument_var_statement(node->pn_left->pn_left, f, 0);  
         break;  
       case TOK_NAME:  
         instrument_expression(node->pn_left->pn_left, f);  
         break;  
       default:  
         /* this is undocumented: for (x.value in y) */  
         instrument_expression(node->pn_left->pn_left, f);  
         break;  
 /*  
       default:  
         fprintf(stderr, "unexpected node type: %d\n", node->pn_left->pn_left->pn_type);  
         abort();  
         break;  
 */  
       }  
       Stream_write_string(f, " in ");  
       instrument_expression(node->pn_left->pn_right, f);  
991        break;        break;
992      case TOK_RESERVED:      case TOK_RESERVED:
993        /* for (;;) */        /* for (;;) */
994        assert(node->pn_left->pn_arity == PN_TERNARY);        assert(node->pn_left->pn_arity == PN_TERNARY);
995          Stream_write_string(f, "for (");
996        if (node->pn_left->pn_kid1) {        if (node->pn_left->pn_kid1) {
997          if (node->pn_left->pn_kid1->pn_type == TOK_VAR) {          output_expression(node->pn_left->pn_kid1, f, false);
           instrument_var_statement(node->pn_left->pn_kid1, f, 0);  
         }  
         else {  
           instrument_expression(node->pn_left->pn_kid1, f);  
         }  
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, ')');
1010        break;        break;
1011      default:      default:
1012        abort();        abort();
1013        break;        break;
1014      }      }
1015      Stream_write_string(f, ") {\n");      Stream_write_string(f, " {\n");
1016      instrument_statement(node->pn_right, f, indent + 2, false);      instrument_statement(node->pn_right, f, indent + 2, false);
1017      Stream_write_string(f, "}\n");      Stream_write_string(f, "}\n");
1018      break;      break;
# Line 839  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 848  Line 1029 
1029      instrument_statement(node->pn_kid1, f, indent + 2, false);      instrument_statement(node->pn_kid1, f, indent + 2, false);
1030      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
1031      Stream_write_string(f, "}\n");      Stream_write_string(f, "}\n");
1032      {      if (node->pn_kid2) {
1033        for (JSParseNode * catch = node->pn_kid2; catch != NULL; catch = catch->pn_kid2) {        assert(node->pn_kid2->pn_type == TOK_RESERVED);
1034          for (JSParseNode * scope = node->pn_kid2->pn_head; scope != NULL; scope = scope->pn_next) {
1035            assert(scope->pn_type == TOK_LEXICALSCOPE);
1036            JSParseNode * catch = scope->pn_expr;
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          assert(catch->pn_kid1->pn_arity == PN_NAME);          output_expression(catch->pn_kid1, f, false);
1041          print_string_atom(catch->pn_kid1->pn_atom, f);          if (catch->pn_kid2) {
         if (catch->pn_kid1->pn_expr) {  
1042            Stream_write_string(f, " if ");            Stream_write_string(f, " if ");
1043            instrument_expression(catch->pn_kid1->pn_expr, 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 892  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, "");
1082      Stream_write_string(f, "}\n");      Stream_write_string(f, "}\n");
1083      break;      break;
1084    case TOK_VAR:    case TOK_VAR:
1085      instrument_var_statement(node, f, indent);      Stream_printf(f, "%*s", indent, "");
1086        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 908  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 916  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;
1134      }
1135      case TOK_LEXICALSCOPE:
1136        /* let statement */
1137        assert(node->pn_arity == PN_NAME);
1138        switch (node->pn_expr->pn_type) {
1139        case TOK_LET:
1140          /* let statement */
1141          assert(node->pn_expr->pn_arity == PN_BINARY);
1142          instrument_statement(node->pn_expr, f, indent, false);
1143          break;
1144        case TOK_LC:
1145          /* block */
1146          Stream_printf(f, "%*s", indent, "");
1147          Stream_write_string(f, "{\n");
1148          instrument_statement(node->pn_expr, f, indent + 2, false);
1149          Stream_printf(f, "%*s", indent, "");
1150          Stream_write_string(f, "}\n");
1151          break;
1152        case TOK_FOR:
1153          instrument_statement(node->pn_expr, f, indent, false);
1154          break;
1155        default:
1156          abort();
1157          break;
1158        }
1159        break;
1160      case TOK_LET:
1161        switch (node->pn_arity) {
1162        case PN_BINARY:
1163          /* let statement */
1164          Stream_printf(f, "%*s", indent, "");
1165          Stream_write_string(f, "let (");
1166          assert(node->pn_left->pn_type == TOK_LP);
1167          assert(node->pn_left->pn_arity == PN_LIST);
1168          instrument_declarations(node->pn_left, f);
1169          Stream_write_string(f, ") {\n");
1170          instrument_statement(node->pn_right, f, indent + 2, false);
1171          Stream_printf(f, "%*s", indent, "");
1172          Stream_write_string(f, "}\n");
1173          break;
1174        case PN_LIST:
1175          /* let definition */
1176          Stream_printf(f, "%*s", indent, "");
1177          output_expression(node, f, false);
1178          Stream_write_string(f, ";\n");
1179          break;
1180        default:
1181          abort();
1182          break;
1183        }
1184        break;
1185      case TOK_DEBUGGER:
1186        Stream_printf(f, "%*s", indent, "");
1187        Stream_write_string(f, "debugger;\n");
1188      break;      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 945  Line 1197 
1197  TOK_EXPORT, TOK_IMPORT are not handled.  TOK_EXPORT, TOK_IMPORT are not handled.
1198  */  */
1199  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) {
1200    if (node->pn_type != TOK_LC) {    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 996  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) {
1255    file_id = id;    file_id = id;
1256    
   /* scan the javascript */  
   JSTokenStream * token_stream = js_NewTokenStream(context, characters, num_characters, NULL, 1, NULL);  
   if (token_stream == NULL) {  
     fatal("cannot create token stream from file: %s", file_id);  
   }  
   
1257    /* parse the javascript */    /* parse the javascript */
1258      JSParseContext parse_context;
1259      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);
1261      }
1262    JSErrorReporter old_error_reporter = JS_SetErrorReporter(context, error_reporter);    JSErrorReporter old_error_reporter = JS_SetErrorReporter(context, error_reporter);
1263    JSParseNode * node = js_ParseTokenStream(context, global, token_stream);    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 1034  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 1097  Line 1347 
1347    
1348    Stream * instrumented = Stream_new(0);    Stream * instrumented = Stream_new(0);
1349    instrument_statement(node, instrumented, 0, false);    instrument_statement(node, instrumented, 0, false);
1350      js_FinishParseContext(context, &parse_context);
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++) {
# Line 1177  Line 1440 
1440            /* line feed (new line) */            /* line feed (new line) */
1441            done = true;            done = true;
1442            break;            break;
1443            /* IE doesn't support this */
1444            /*
1445          case 0xb:          case 0xb:
           /* vertical tab */  
1446            Stream_write_string(output, "\\v");            Stream_write_string(output, "\\v");
1447            break;            break;
1448            */
1449          case 0xc:          case 0xc:
1450            /* form feed */            /* form feed */
1451            Stream_write_string(output, "\\f");            Stream_write_string(output, "\\f");
# Line 1235  Line 1500 
1500            /* line feed (new line) */            /* line feed (new line) */
1501            done = true;            done = true;
1502            break;            break;
1503            /* IE doesn't support this */
1504            /*
1505          case 0xb:          case 0xb:
           /* vertical tab */  
1506            Stream_write_string(output, "\\v");            Stream_write_string(output, "\\v");
1507            break;            break;
1508            */
1509          case 0xc:          case 0xc:
1510            /* form feed */            /* form feed */
1511            Stream_write_string(output, "\\f");            Stream_write_string(output, "\\f");
# Line 1365  Line 1632 
1632  }  }
1633    
1634  int jscoverage_parse_json(Coverage * coverage, const uint8_t * json, size_t length) {  int jscoverage_parse_json(Coverage * coverage, const uint8_t * json, size_t length) {
1635      int result = 0;
1636    
1637    jschar * base = js_InflateString(context, (char *) json, &length);    jschar * base = js_InflateString(context, (char *) json, &length);
1638    if (base == NULL) {    if (base == NULL) {
1639      fatal("out of memory");      fatal("out of memory");
# Line 1377  Line 1646 
1646    
1647    JS_free(context, base);    JS_free(context, base);
1648    
1649    JSTokenStream * token_stream = js_NewTokenStream(context, parenthesized_json, length + 2, NULL, 1, NULL);    JSParseContext parse_context;
1650    if (token_stream == NULL) {    if (! js_InitParseContext(context, &parse_context, NULL, NULL, parenthesized_json, length + 2, NULL, NULL, 1)) {
1651      fatal("cannot create token stream");      free(parenthesized_json);
1652        return -1;
1653    }    }
1654      JSParseNode * root = js_ParseScript(context, global, &parse_context);
   JSParseNode * root = js_ParseTokenStream(context, global, token_stream);  
1655    free(parenthesized_json);    free(parenthesized_json);
1656    if (root == NULL) {    if (root == NULL) {
1657      return -1;      result = -1;
1658        goto done;
1659    }    }
1660    
1661    /* root node must be TOK_LC */    /* root node must be TOK_LC */
1662    if (root->pn_type != TOK_LC) {    if (root->pn_type != TOK_LC) {
1663      return -1;      result = -1;
1664        goto done;
1665    }    }
1666    JSParseNode * semi = root->pn_u.list.head;    JSParseNode * semi = root->pn_u.list.head;
1667    
1668    /* the list must be TOK_SEMI and it must contain only one element */    /* the list must be TOK_SEMI and it must contain only one element */
1669    if (semi->pn_type != TOK_SEMI || semi->pn_next != NULL) {    if (semi->pn_type != TOK_SEMI || semi->pn_next != NULL) {
1670      return -1;      result = -1;
1671        goto done;
1672    }    }
1673    JSParseNode * parenthesized = semi->pn_kid;    JSParseNode * parenthesized = semi->pn_kid;
1674    
1675    /* this must be a parenthesized expression */    /* this must be a parenthesized expression */
1676    if (parenthesized->pn_type != TOK_RP) {    if (parenthesized->pn_type != TOK_RP) {
1677      return -1;      result = -1;
1678        goto done;
1679    }    }
1680    JSParseNode * object = parenthesized->pn_kid;    JSParseNode * object = parenthesized->pn_kid;
1681    
1682    /* this must be an object literal */    /* this must be an object literal */
1683    if (object->pn_type != TOK_RC) {    if (object->pn_type != TOK_RC) {
1684      return -1;      result = -1;
1685        goto done;
1686    }    }
1687    
1688    for (JSParseNode * p = object->pn_head; p != NULL; p = p->pn_next) {    for (JSParseNode * p = object->pn_head; p != NULL; p = p->pn_next) {
1689      /* every element of this list must be TOK_COLON */      /* every element of this list must be TOK_COLON */
1690      if (p->pn_type != TOK_COLON) {      if (p->pn_type != TOK_COLON) {
1691        return -1;        result = -1;
1692          goto done;
1693      }      }
1694    
1695      /* the key must be a string representing the file */      /* the key must be a string representing the file */
1696      JSParseNode * key = p->pn_left;      JSParseNode * key = p->pn_left;
1697      if (key->pn_type != TOK_STRING || ! ATOM_IS_STRING(key->pn_atom)) {      if (key->pn_type != TOK_STRING || ! ATOM_IS_STRING(key->pn_atom)) {
1698        return -1;        result = -1;
1699          goto done;
1700      }      }
1701      char * id_bytes = JS_GetStringBytes(ATOM_TO_STRING(key->pn_atom));      char * id_bytes = JS_GetStringBytes(ATOM_TO_STRING(key->pn_atom));
1702    
1703      /* the value must be an object literal OR an array */      /* the value must be an object literal OR an array */
1704      JSParseNode * value = p->pn_right;      JSParseNode * value = p->pn_right;
1705      if (! (value->pn_type == TOK_RC || value->pn_type == TOK_RB)) {      if (! (value->pn_type == TOK_RC || value->pn_type == TOK_RB)) {
1706        return -1;        result = -1;
1707          goto done;
1708      }      }
1709    
1710      JSParseNode * array = NULL;      JSParseNode * array = NULL;
# Line 1439  Line 1716 
1716      else if (value->pn_type == TOK_RC) {      else if (value->pn_type == TOK_RC) {
1717        /* an object literal */        /* an object literal */
1718        if (value->pn_count != 2) {        if (value->pn_count != 2) {
1719          return -1;          result = -1;
1720            goto done;
1721        }        }
1722        for (JSParseNode * element = value->pn_head; element != NULL; element = element->pn_next) {        for (JSParseNode * element = value->pn_head; element != NULL; element = element->pn_next) {
1723          if (element->pn_type != TOK_COLON) {          if (element->pn_type != TOK_COLON) {
1724            return -1;            result = -1;
1725              goto done;
1726          }          }
1727          JSParseNode * left = element->pn_left;          JSParseNode * left = element->pn_left;
1728          if (left->pn_type != TOK_STRING || ! ATOM_IS_STRING(left->pn_atom)) {          if (left->pn_type != TOK_STRING || ! ATOM_IS_STRING(left->pn_atom)) {
1729            return -1;            result = -1;
1730              goto done;
1731          }          }
1732          const char * s = JS_GetStringBytes(ATOM_TO_STRING(left->pn_atom));          const char * s = JS_GetStringBytes(ATOM_TO_STRING(left->pn_atom));
1733          if (strcmp(s, "coverage") == 0) {          if (strcmp(s, "coverage") == 0) {
1734            array = element->pn_right;            array = element->pn_right;
1735            if (array->pn_type != TOK_RB) {            if (array->pn_type != TOK_RB) {
1736              return -1;              result = -1;
1737                goto done;
1738            }            }
1739          }          }
1740          else if (strcmp(s, "source") == 0) {          else if (strcmp(s, "source") == 0) {
1741            source = element->pn_right;            source = element->pn_right;
1742            if (source->pn_type != TOK_RB) {            if (source->pn_type != TOK_RB) {
1743              return -1;              result = -1;
1744                goto done;
1745            }            }
1746          }          }
1747          else {          else {
1748            return -1;            result = -1;
1749              goto done;
1750          }          }
1751        }        }
1752      }      }
1753      else {      else {
1754        return -1;        result = -1;
1755          goto done;
1756      }      }
1757    
1758      if (array == NULL) {      if (array == NULL) {
1759        return -1;        result = -1;
1760          goto done;
1761      }      }
1762    
1763      /* look up the file in the coverage table */      /* look up the file in the coverage table */
# Line 1496  Line 1781 
1781            file_coverage->coverage_lines[i] = -1;            file_coverage->coverage_lines[i] = -1;
1782          }          }
1783          else {          else {
1784            return -1;            result = -1;
1785              goto done;
1786          }          }
1787        }        }
1788        assert(i == array->pn_count);        assert(i == array->pn_count);
# Line 1512  Line 1798 
1798        /* sanity check */        /* sanity check */
1799        assert(strcmp(file_coverage->id, id_bytes) == 0);        assert(strcmp(file_coverage->id, id_bytes) == 0);
1800        if (file_coverage->num_coverage_lines != array->pn_count) {        if (file_coverage->num_coverage_lines != array->pn_count) {
1801          return -2;          result = -2;
1802            goto done;
1803        }        }
1804    
1805        /* merge the coverage */        /* merge the coverage */
# Line 1520  Line 1807 
1807        for (JSParseNode * element = array->pn_head; element != NULL; element = element->pn_next, i++) {        for (JSParseNode * element = array->pn_head; element != NULL; element = element->pn_next, i++) {
1808          if (element->pn_type == TOK_NUMBER) {          if (element->pn_type == TOK_NUMBER) {
1809            if (file_coverage->coverage_lines[i] == -1) {            if (file_coverage->coverage_lines[i] == -1) {
1810              return -2;              result = -2;
1811                goto done;
1812            }            }
1813            file_coverage->coverage_lines[i] += (int) element->pn_dval;            file_coverage->coverage_lines[i] += (int) element->pn_dval;
1814          }          }
1815          else if (element->pn_type == TOK_PRIMARY && element->pn_op == JSOP_NULL) {          else if (element->pn_type == TOK_PRIMARY && element->pn_op == JSOP_NULL) {
1816            if (file_coverage->coverage_lines[i] != -1) {            if (file_coverage->coverage_lines[i] != -1) {
1817              return -2;              result = -2;
1818                goto done;
1819            }            }
1820          }          }
1821          else {          else {
1822            return -1;            result = -1;
1823              goto done;
1824          }          }
1825        }        }
1826        assert(i == array->pn_count);        assert(i == array->pn_count);
# Line 1543  Line 1833 
1833        uint32 i = 0;        uint32 i = 0;
1834        for (JSParseNode * element = source->pn_head; element != NULL; element = element->pn_next, i++) {        for (JSParseNode * element = source->pn_head; element != NULL; element = element->pn_next, i++) {
1835          if (element->pn_type != TOK_STRING) {          if (element->pn_type != TOK_STRING) {
1836            return -1;            result = -1;
1837              goto done;
1838          }          }
1839          file_coverage->source_lines[i] = xstrdup(JS_GetStringBytes(ATOM_TO_STRING(element->pn_atom)));          file_coverage->source_lines[i] = xstrdup(JS_GetStringBytes(ATOM_TO_STRING(element->pn_atom)));
1840        }        }
# Line 1551  Line 1842 
1842      }      }
1843    }    }
1844    
1845    return 0;  done:
1846      js_FinishParseContext(context, &parse_context);
1847      return result;
1848  }  }

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

  ViewVC Help
Powered by ViewVC 1.1.24