/[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 389 by siliconforks, Thu Oct 30 17:53:07 2008 UTC
# Line 22  Line 22 
22  #include "instrument-js.h"  #include "instrument-js.h"
23    
24  #include <assert.h>  #include <assert.h>
25    #include <math.h>
26  #include <stdlib.h>  #include <stdlib.h>
27  #include <string.h>  #include <string.h>
28    
29  #include <jsapi.h>  #include <jsapi.h>
30    #include <jsarena.h>
31  #include <jsatom.h>  #include <jsatom.h>
32    #include <jsemit.h>
33  #include <jsexn.h>  #include <jsexn.h>
34  #include <jsfun.h>  #include <jsfun.h>
35  #include <jsinterp.h>  #include <jsinterp.h>
36    #include <jsiter.h>
37  #include <jsparse.h>  #include <jsparse.h>
38  #include <jsregexp.h>  #include <jsregexp.h>
39  #include <jsscope.h>  #include <jsscope.h>
# Line 49  Line 53 
53    struct IfDirective * next;    struct IfDirective * next;
54  };  };
55    
56    bool jscoverage_mozilla = false;
57    
58  static bool * exclusive_directives = NULL;  static bool * exclusive_directives = NULL;
59    
60  static JSRuntime * runtime = NULL;  static JSRuntime * runtime = NULL;
61  static JSContext * context = NULL;  static JSContext * context = NULL;
62  static JSObject * global = NULL;  static JSObject * global = NULL;
63    static JSVersion js_version = JSVERSION_ECMA_3;
64    
65  /*  /*
66  JSParseNode objects store line numbers starting from 1.  JSParseNode objects store line numbers starting from 1.
# Line 63  Line 70 
70  static char * lines = NULL;  static char * lines = NULL;
71  static uint16_t num_lines = 0;  static uint16_t num_lines = 0;
72    
73    void jscoverage_set_js_version(const char * version) {
74      js_version = JS_StringToVersion(version);
75      if (js_version != JSVERSION_UNKNOWN) {
76        return;
77      }
78    
79      char * end;
80      js_version = (JSVersion) strtol(version, &end, 10);
81      if ((size_t) (end - version) != strlen(version)) {
82        fatal("invalid version: %s", version);
83      }
84    }
85    
86  void jscoverage_init(void) {  void jscoverage_init(void) {
87    runtime = JS_NewRuntime(8L * 1024L * 1024L);    runtime = JS_NewRuntime(8L * 1024L * 1024L);
88    if (runtime == NULL) {    if (runtime == NULL) {
# Line 74  Line 94 
94      fatal("cannot create context");      fatal("cannot create context");
95    }    }
96    
97      JS_SetVersion(context, js_version);
98    
99    global = JS_NewObject(context, NULL, NULL, NULL);    global = JS_NewObject(context, NULL, NULL, NULL);
100    if (global == NULL) {    if (global == NULL) {
101      fatal("cannot create global object");      fatal("cannot create global object");
# Line 147  Line 169 
169        case 0xa:        case 0xa:
170          Stream_write_string(f, "\\n");          Stream_write_string(f, "\\n");
171          break;          break;
172          /* IE doesn't support this */
173          /*
174        case 0xb:        case 0xb:
175          Stream_write_string(f, "\\v");          Stream_write_string(f, "\\v");
176          break;          break;
177          */
178        case 0xc:        case 0xc:
179          Stream_write_string(f, "\\f");          Stream_write_string(f, "\\f");
180          break;          break;
# Line 196  Line 221 
221    
222  static const char * get_op(uint8 op) {  static const char * get_op(uint8 op) {
223    switch(op) {    switch(op) {
224      case JSOP_OR:
225        return "||";
226      case JSOP_AND:
227        return "&&";
228    case JSOP_BITOR:    case JSOP_BITOR:
229      return "|";      return "|";
230    case JSOP_BITXOR:    case JSOP_BITXOR:
# Line 206  Line 235 
235      return "==";      return "==";
236    case JSOP_NE:    case JSOP_NE:
237      return "!=";      return "!=";
238    case JSOP_NEW_EQ:    case JSOP_STRICTEQ:
239      return "===";      return "===";
240    case JSOP_NEW_NE:    case JSOP_STRICTNE:
241      return "!==";      return "!==";
242    case JSOP_LT:    case JSOP_LT:
243      return "<";      return "<";
# Line 239  Line 268 
268    }    }
269  }  }
270    
271  static void instrument_expression(JSParseNode * node, Stream * f);  static void output_expression(JSParseNode * node, Stream * f, bool parenthesize_object_literals);
272  static void instrument_statement(JSParseNode * node, Stream * f, int indent, bool is_jscoverage_if);  static void instrument_statement(JSParseNode * node, Stream * f, int indent, bool is_jscoverage_if);
273    static void output_statement(JSParseNode * node, Stream * f, int indent, bool is_jscoverage_if);
274    
275  enum FunctionType {  enum FunctionType {
276    FUNCTION_NORMAL,    FUNCTION_NORMAL,
277    FUNCTION_GETTER_OR_SETTER    FUNCTION_GETTER_OR_SETTER
278  };  };
279    
280    static void output_for_in(JSParseNode * node, Stream * f) {
281      assert(node->pn_type == TOK_FOR);
282      assert(node->pn_arity == PN_BINARY);
283      Stream_write_string(f, "for ");
284      if (node->pn_iflags & JSITER_FOREACH) {
285        Stream_write_string(f, "each ");
286      }
287      Stream_write_char(f, '(');
288      output_expression(node->pn_left, f, false);
289      Stream_write_char(f, ')');
290    }
291    
292    static void output_array_comprehension_or_generator_expression(JSParseNode * node, Stream * f) {
293      assert(node->pn_type == TOK_LEXICALSCOPE);
294      assert(node->pn_arity == PN_NAME);
295      JSParseNode * for_node = node->pn_expr;
296      assert(for_node->pn_type == TOK_FOR);
297      assert(for_node->pn_arity == PN_BINARY);
298      JSParseNode * p = for_node;
299      while (p->pn_type == TOK_FOR) {
300        p = p->pn_right;
301      }
302      JSParseNode * if_node = NULL;
303      if (p->pn_type == TOK_IF) {
304        if_node = p;
305        assert(if_node->pn_arity == PN_TERNARY);
306        p = if_node->pn_kid2;
307      }
308      assert(p->pn_arity == PN_UNARY);
309      p = p->pn_kid;
310      if (p->pn_type == TOK_YIELD) {
311        /* for generator expressions */
312        p = p->pn_kid;
313      }
314    
315      output_expression(p, f, false);
316      p = for_node;
317      while (p->pn_type == TOK_FOR) {
318        Stream_write_char(f, ' ');
319        output_for_in(p, f);
320        p = p->pn_right;
321      }
322      if (if_node) {
323        Stream_write_string(f, " if (");
324        output_expression(if_node->pn_kid1, f, false);
325        Stream_write_char(f, ')');
326      }
327    }
328    
329  static void instrument_function(JSParseNode * node, Stream * f, int indent, enum FunctionType type) {  static void instrument_function(JSParseNode * node, Stream * f, int indent, enum FunctionType type) {
330      assert(node->pn_type == TOK_FUNCTION);
331    assert(node->pn_arity == PN_FUNC);    assert(node->pn_arity == PN_FUNC);
332    assert(ATOM_IS_OBJECT(node->pn_funAtom));    JSObject * object = node->pn_funpob->object;
   JSObject * object = ATOM_TO_OBJECT(node->pn_funAtom);  
333    assert(JS_ObjectIsFunction(context, object));    assert(JS_ObjectIsFunction(context, object));
334    JSFunction * function = (JSFunction *) JS_GetPrivate(context, object);    JSFunction * function = (JSFunction *) JS_GetPrivate(context, object);
335    assert(function);    assert(function);
336    assert(object == function->object);    assert(object == &function->object);
337    Stream_printf(f, "%*s", indent, "");    Stream_printf(f, "%*s", indent, "");
338    if (type == FUNCTION_NORMAL) {    if (type == FUNCTION_NORMAL) {
339      Stream_write_string(f, "function");      Stream_write_string(f, "function ");
340    }    }
341    
342    /* function name */    /* function name */
343    if (function->atom) {    if (function->atom) {
     Stream_write_char(f, ' ');  
344      print_string_atom(function->atom, f);      print_string_atom(function->atom, f);
345    }    }
346    
347    /* function parameters */    /*
348    Stream_write_string(f, "(");    function parameters - see JS_DecompileFunction in jsapi.cpp, which calls
349    JSAtom ** params = xnew(JSAtom *, function->nargs);    js_DecompileFunction in jsopcode.cpp
350    for (int i = 0; i < function->nargs; i++) {    */
351      /* initialize to NULL for sanity check */    Stream_write_char(f, '(');
352      params[i] = NULL;    JSArenaPool pool;
353    }    JS_INIT_ARENA_POOL(&pool, "instrument_function", 256, 1, &context->scriptStackQuota);
354    JSScope * scope = OBJ_SCOPE(object);    jsuword * local_names = NULL;
355    for (JSScopeProperty * scope_property = SCOPE_LAST_PROP(scope); scope_property != NULL; scope_property = scope_property->parent) {    if (JS_GET_LOCAL_NAME_COUNT(function)) {
356      if (scope_property->getter != js_GetArgument) {      local_names = js_GetLocalNameArray(context, function, &pool);
357        continue;      if (local_names == NULL) {
358          fatal("out of memory");
359      }      }
     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);  
360    }    }
361      bool destructuring = false;
362    for (int i = 0; i < function->nargs; i++) {    for (int i = 0; i < function->nargs; i++) {
     assert(params[i] != NULL);  
363      if (i > 0) {      if (i > 0) {
364        Stream_write_string(f, ", ");        Stream_write_string(f, ", ");
365      }      }
366      if (ATOM_IS_STRING(params[i])) {      JSAtom * param = JS_LOCAL_NAME_TO_ATOM(local_names[i]);
367        print_string_atom(params[i], f);      if (param == NULL) {
368          destructuring = true;
369          JSParseNode * expression = NULL;
370          assert(node->pn_body->pn_type == TOK_LC || node->pn_body->pn_type == TOK_BODY);
371          JSParseNode * semi = node->pn_body->pn_head;
372          assert(semi->pn_type == TOK_SEMI);
373          JSParseNode * comma = semi->pn_kid;
374          assert(comma->pn_type == TOK_COMMA);
375          for (JSParseNode * p = comma->pn_head; p != NULL; p = p->pn_next) {
376            assert(p->pn_type == TOK_ASSIGN);
377            JSParseNode * rhs = p->pn_right;
378            assert(JSSTRING_LENGTH(ATOM_TO_STRING(rhs->pn_atom)) == 0);
379            if (rhs->pn_slot == i) {
380              expression = p->pn_left;
381              break;
382            }
383          }
384          assert(expression != NULL);
385          output_expression(expression, f, false);
386        }
387        else {
388          print_string_atom(param, f);
389      }      }
390    }    }
391      JS_FinishArenaPool(&pool);
392    Stream_write_string(f, ") {\n");    Stream_write_string(f, ") {\n");
   free(params);  
393    
394    /* function body */    /* function body */
395    instrument_statement(node->pn_body, f, indent + 2, false);    if (function->flags & JSFUN_EXPR_CLOSURE) {
396        /* expression closure - use output_statement instead of instrument_statement */
397        if (node->pn_body->pn_type == TOK_BODY) {
398          assert(node->pn_body->pn_arity == PN_LIST);
399          assert(node->pn_body->pn_count == 2);
400          output_statement(node->pn_body->pn_head->pn_next, f, indent + 2, false);
401        }
402        else {
403          output_statement(node->pn_body, f, indent + 2, false);
404        }
405      }
406      else {
407        assert(node->pn_body->pn_type == TOK_LC);
408        assert(node->pn_body->pn_arity == PN_LIST);
409        JSParseNode * p = node->pn_body->pn_head;
410        if (destructuring) {
411          p = p->pn_next;
412        }
413        for (; p != NULL; p = p->pn_next) {
414          instrument_statement(p, f, indent + 2, false);
415        }
416      }
417    
418    Stream_write_string(f, "}\n");    Stream_write_char(f, '}');
419  }  }
420    
421  static void instrument_function_call(JSParseNode * node, Stream * f) {  static void instrument_function_call(JSParseNode * node, Stream * f) {
422    instrument_expression(node->pn_head, f);    JSParseNode * function_node = node->pn_head;
423      if (function_node->pn_type == TOK_FUNCTION) {
424        JSObject * object = function_node->pn_funpob->object;
425        assert(JS_ObjectIsFunction(context, object));
426        JSFunction * function = (JSFunction *) JS_GetPrivate(context, object);
427        assert(function);
428        assert(object == &function->object);
429    
430        if (function_node->pn_flags & TCF_GENEXP_LAMBDA) {
431          /* it's a generator expression */
432          Stream_write_char(f, '(');
433          output_array_comprehension_or_generator_expression(function_node->pn_body, f);
434          Stream_write_char(f, ')');
435          return;
436        }
437      }
438      output_expression(function_node, f, false);
439    Stream_write_char(f, '(');    Stream_write_char(f, '(');
440    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) {
441      if (p != node->pn_head->pn_next) {      if (p != node->pn_head->pn_next) {
442        Stream_write_string(f, ", ");        Stream_write_string(f, ", ");
443      }      }
444      instrument_expression(p, f);      output_expression(p, f, false);
445    }    }
446    Stream_write_char(f, ')');    Stream_write_char(f, ')');
447  }  }
448    
449    static void instrument_declarations(JSParseNode * list, Stream * f) {
450      assert(list->pn_arity == PN_LIST);
451      for (JSParseNode * p = list->pn_head; p != NULL; p = p->pn_next) {
452        if (p != list->pn_head) {
453          Stream_write_string(f, ", ");
454        }
455        output_expression(p, f, false);
456      }
457    }
458    
459  /*  /*
460  See <Expressions> in jsparse.h.  See <Expressions> in jsparse.h.
461  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 469 
469  TOK_INSTANCEOF  binary  TOK_INSTANCEOF  binary
470  TOK_IN          binary  TOK_IN          binary
471  */  */
472  static void instrument_expression(JSParseNode * node, Stream * f) {  static void output_expression(JSParseNode * node, Stream * f, bool parenthesize_object_literals) {
473    switch (node->pn_type) {    switch (node->pn_type) {
474    case TOK_FUNCTION:    case TOK_FUNCTION:
475        Stream_write_char(f, '(');
476      instrument_function(node, f, 0, FUNCTION_NORMAL);      instrument_function(node, f, 0, FUNCTION_NORMAL);
477        Stream_write_char(f, ')');
478      break;      break;
479    case TOK_COMMA:    case TOK_COMMA:
480      for (struct JSParseNode * p = node->pn_head; p != NULL; p = p->pn_next) {      for (struct JSParseNode * p = node->pn_head; p != NULL; p = p->pn_next) {
481        if (p != node->pn_head) {        if (p != node->pn_head) {
482          Stream_write_string(f, ", ");          Stream_write_string(f, ", ");
483        }        }
484        instrument_expression(p, f);        output_expression(p, f, parenthesize_object_literals);
485      }      }
486      break;      break;
487    case TOK_ASSIGN:    case TOK_ASSIGN:
488      instrument_expression(node->pn_left, f);      output_expression(node->pn_left, f, parenthesize_object_literals);
489      Stream_write_char(f, ' ');      Stream_write_char(f, ' ');
490      switch (node->pn_op) {      switch (node->pn_op) {
491      case JSOP_ADD:      case JSOP_ADD:
# Line 361  Line 506 
506        break;        break;
507      }      }
508      Stream_write_string(f, "= ");      Stream_write_string(f, "= ");
509      instrument_expression(node->pn_right, f);      output_expression(node->pn_right, f, false);
510      break;      break;
511    case TOK_HOOK:    case TOK_HOOK:
512      instrument_expression(node->pn_kid1, f);      output_expression(node->pn_kid1, f, parenthesize_object_literals);
513      Stream_write_string(f, "? ");      Stream_write_string(f, "? ");
514      instrument_expression(node->pn_kid2, f);      output_expression(node->pn_kid2, f, false);
515      Stream_write_string(f, ": ");      Stream_write_string(f, ": ");
516      instrument_expression(node->pn_kid3, f);      output_expression(node->pn_kid3, f, false);
517      break;      break;
518    case TOK_OR:    case TOK_OR:
     instrument_expression(node->pn_left, f);  
     Stream_write_string(f, " || ");  
     instrument_expression(node->pn_right, f);  
     break;  
519    case TOK_AND:    case TOK_AND:
     instrument_expression(node->pn_left, f);  
     Stream_write_string(f, " && ");  
     instrument_expression(node->pn_right, f);  
     break;  
520    case TOK_BITOR:    case TOK_BITOR:
521    case TOK_BITXOR:    case TOK_BITXOR:
522    case TOK_BITAND:    case TOK_BITAND:
# Line 392  Line 529 
529    case TOK_DIVOP:    case TOK_DIVOP:
530      switch (node->pn_arity) {      switch (node->pn_arity) {
531      case PN_BINARY:      case PN_BINARY:
532        instrument_expression(node->pn_left, f);        output_expression(node->pn_left, f, parenthesize_object_literals);
533        Stream_printf(f, " %s ", get_op(node->pn_op));        Stream_printf(f, " %s ", get_op(node->pn_op));
534        instrument_expression(node->pn_right, f);        output_expression(node->pn_right, f, false);
535        break;        break;
536      case PN_LIST:      case PN_LIST:
537        for (struct JSParseNode * p = node->pn_head; p != NULL; p = p->pn_next) {        for (struct JSParseNode * p = node->pn_head; p != NULL; p = p->pn_next) {
538          if (p != node->pn_head) {          if (p == node->pn_head) {
539              output_expression(p, f, parenthesize_object_literals);
540            }
541            else {
542            Stream_printf(f, " %s ", get_op(node->pn_op));            Stream_printf(f, " %s ", get_op(node->pn_op));
543              output_expression(p, f, false);
544          }          }
         instrument_expression(p, f);  
545        }        }
546        break;        break;
547      default:      default:
# Line 411  Line 551 
551    case TOK_UNARYOP:    case TOK_UNARYOP:
552      switch (node->pn_op) {      switch (node->pn_op) {
553      case JSOP_NEG:      case JSOP_NEG:
554        Stream_write_char(f, '-');        Stream_write_string(f, "- ");
555        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, false);
556        break;        break;
557      case JSOP_POS:      case JSOP_POS:
558        Stream_write_char(f, '+');        Stream_write_string(f, "+ ");
559        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, false);
560        break;        break;
561      case JSOP_NOT:      case JSOP_NOT:
562        Stream_write_char(f, '!');        Stream_write_string(f, "! ");
563        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, false);
564        break;        break;
565      case JSOP_BITNOT:      case JSOP_BITNOT:
566        Stream_write_char(f, '~');        Stream_write_string(f, "~ ");
567        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, false);
568        break;        break;
569      case JSOP_TYPEOF:      case JSOP_TYPEOF:
570        Stream_write_string(f, "typeof ");        Stream_write_string(f, "typeof ");
571        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, false);
572        break;        break;
573      case JSOP_VOID:      case JSOP_VOID:
574        Stream_write_string(f, "void ");        Stream_write_string(f, "void ");
575        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, false);
576        break;        break;
577      default:      default:
578        abort();        fatal_source(file_id, node->pn_pos.begin.lineno, "unknown operator (%d)", node->pn_op);
579        break;        break;
580      }      }
581      break;      break;
# Line 449  Line 589 
589      case JSOP_INCPROP:      case JSOP_INCPROP:
590      case JSOP_INCELEM:      case JSOP_INCELEM:
591        Stream_write_string(f, "++");        Stream_write_string(f, "++");
592        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, false);
593        break;        break;
594      case JSOP_DECNAME:      case JSOP_DECNAME:
595      case JSOP_DECPROP:      case JSOP_DECPROP:
596      case JSOP_DECELEM:      case JSOP_DECELEM:
597        Stream_write_string(f, "--");        Stream_write_string(f, "--");
598        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, false);
599        break;        break;
600      case JSOP_NAMEINC:      case JSOP_NAMEINC:
601      case JSOP_PROPINC:      case JSOP_PROPINC:
602      case JSOP_ELEMINC:      case JSOP_ELEMINC:
603        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, parenthesize_object_literals);
604        Stream_write_string(f, "++");        Stream_write_string(f, "++");
605        break;        break;
606      case JSOP_NAMEDEC:      case JSOP_NAMEDEC:
607      case JSOP_PROPDEC:      case JSOP_PROPDEC:
608      case JSOP_ELEMDEC:      case JSOP_ELEMDEC:
609        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, parenthesize_object_literals);
610        Stream_write_string(f, "--");        Stream_write_string(f, "--");
611        break;        break;
612      default:      default:
# Line 480  Line 620 
620      break;      break;
621    case TOK_DELETE:    case TOK_DELETE:
622      Stream_write_string(f, "delete ");      Stream_write_string(f, "delete ");
623      instrument_expression(node->pn_kid, f);      output_expression(node->pn_kid, f, false);
624      break;      break;
625    case TOK_DOT:    case TOK_DOT:
626        /* numeric literals must be parenthesized */
627        switch (node->pn_expr->pn_type) {
628        case TOK_NUMBER:
629          Stream_write_char(f, '(');
630          output_expression(node->pn_expr, f, false);
631          Stream_write_char(f, ')');
632          break;
633        default:
634          output_expression(node->pn_expr, f, true);
635          break;
636        }
637      /*      /*
638      This may have originally been x['foo-bar'].  Because the string 'foo-bar'      This may have originally been x['foo-bar'].  Because the string 'foo-bar'
639      contains illegal characters, we have to use the subscript syntax instead of      contains illegal characters, we have to use the subscript syntax instead of
640      the dot syntax.      the dot syntax.
641      */      */
     instrument_expression(node->pn_expr, f);  
642      assert(ATOM_IS_STRING(node->pn_atom));      assert(ATOM_IS_STRING(node->pn_atom));
643      {      {
644        JSString * s = ATOM_TO_STRING(node->pn_atom);        JSString * s = ATOM_TO_STRING(node->pn_atom);
645        /* XXX - semantics changed in 1.7 */        bool must_quote;
646        if (! ATOM_KEYWORD(node->pn_atom) && js_IsIdentifier(s)) {        if (JSSTRING_LENGTH(s) == 0) {
647          Stream_write_char(f, '.');          must_quote = true;
648          print_string_atom(node->pn_atom, f);        }
649          else if (js_CheckKeyword(JSSTRING_CHARS(s), JSSTRING_LENGTH(s)) != TOK_EOF) {
650            must_quote = true;
651          }
652          else if (! js_IsIdentifier(s)) {
653            must_quote = true;
654        }        }
655        else {        else {
656            must_quote = false;
657          }
658          if (must_quote) {
659          Stream_write_char(f, '[');          Stream_write_char(f, '[');
660          print_quoted_string_atom(node->pn_atom, f);          print_quoted_string_atom(node->pn_atom, f);
661          Stream_write_char(f, ']');          Stream_write_char(f, ']');
662        }        }
663          else {
664            Stream_write_char(f, '.');
665            print_string_atom(node->pn_atom, f);
666          }
667      }      }
668      break;      break;
669    case TOK_LB:    case TOK_LB:
670      instrument_expression(node->pn_left, f);      output_expression(node->pn_left, f, false);
671      Stream_write_char(f, '[');      Stream_write_char(f, '[');
672      instrument_expression(node->pn_right, f);      output_expression(node->pn_right, f, false);
673      Stream_write_char(f, ']');      Stream_write_char(f, ']');
674      break;      break;
675    case TOK_LP:    case TOK_LP:
# Line 521  Line 683 
683        }        }
684        /* TOK_COMMA is a special case: a hole in the array */        /* TOK_COMMA is a special case: a hole in the array */
685        if (p->pn_type != TOK_COMMA) {        if (p->pn_type != TOK_COMMA) {
686          instrument_expression(p, f);          output_expression(p, f, false);
687        }        }
688      }      }
689      if (node->pn_extra == PNX_ENDCOMMA) {      if (node->pn_extra == PNX_ENDCOMMA) {
# Line 530  Line 692 
692      Stream_write_char(f, ']');      Stream_write_char(f, ']');
693      break;      break;
694    case TOK_RC:    case TOK_RC:
695        if (parenthesize_object_literals) {
696          Stream_write_char(f, '(');
697        }
698      Stream_write_char(f, '{');      Stream_write_char(f, '{');
699      for (struct JSParseNode * p = node->pn_head; p != NULL; p = p->pn_next) {      for (struct JSParseNode * p = node->pn_head; p != NULL; p = p->pn_next) {
700        assert(p->pn_type == TOK_COLON);        if (p->pn_type != TOK_COLON) {
701            fatal_source(file_id, p->pn_pos.begin.lineno, "unsupported node type (%d)", p->pn_type);
702          }
703        if (p != node->pn_head) {        if (p != node->pn_head) {
704          Stream_write_string(f, ", ");          Stream_write_string(f, ", ");
705        }        }
# Line 547  Line 714 
714          else {          else {
715            Stream_write_string(f, "set ");            Stream_write_string(f, "set ");
716          }          }
717          instrument_expression(p->pn_left, f);          output_expression(p->pn_left, f, false);
718          if (p->pn_right->pn_type != TOK_FUNCTION) {          if (p->pn_right->pn_type != TOK_FUNCTION) {
719            fatal("parse error: expected function");            fatal_source(file_id, p->pn_pos.begin.lineno, "expected function");
720          }          }
721          instrument_function(p->pn_right, f, 0, FUNCTION_GETTER_OR_SETTER);          instrument_function(p->pn_right, f, 0, FUNCTION_GETTER_OR_SETTER);
722          break;          break;
723        default:        default:
724          instrument_expression(p->pn_left, f);          output_expression(p->pn_left, f, false);
725          Stream_write_string(f, ": ");          Stream_write_string(f, ": ");
726          instrument_expression(p->pn_right, f);          output_expression(p->pn_right, f, false);
727          break;          break;
728        }        }
729      }      }
730      Stream_write_char(f, '}');      Stream_write_char(f, '}');
731        if (parenthesize_object_literals) {
732          Stream_write_char(f, ')');
733        }
734      break;      break;
735    case TOK_RP:    case TOK_RP:
736      Stream_write_char(f, '(');      Stream_write_char(f, '(');
737      instrument_expression(node->pn_kid, f);      output_expression(node->pn_kid, f, false);
738      Stream_write_char(f, ')');      Stream_write_char(f, ')');
739      break;      break;
740    case TOK_NAME:    case TOK_NAME:
741      print_string_atom(node->pn_atom, f);      print_string_atom(node->pn_atom, f);
742        if (node->pn_expr != NULL) {
743          Stream_write_string(f, " = ");
744          output_expression(node->pn_expr, f, false);
745        }
746      break;      break;
747    case TOK_STRING:    case TOK_STRING:
748      print_quoted_string_atom(node->pn_atom, f);      print_quoted_string_atom(node->pn_atom, f);
749      break;      break;
750    case TOK_OBJECT:    case TOK_REGEXP:
751      switch (node->pn_op) {      assert(node->pn_op == JSOP_REGEXP);
752      case JSOP_OBJECT:      {
753        /* I assume this is JSOP_REGEXP */        JSObject * object = node->pn_pob->object;
754        abort();        jsval result;
755        break;        js_regexp_toString(context, object, &result);
756      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;  
757      }      }
758      break;      break;
759    case TOK_NUMBER:    case TOK_NUMBER:
# Line 603  Line 766 
766      if (node->pn_dval == 0.0) {      if (node->pn_dval == 0.0) {
767        Stream_write_string(f, "0");        Stream_write_string(f, "0");
768      }      }
769        else if (node->pn_dval == INFINITY) {
770          Stream_write_string(f, "Number.POSITIVE_INFINITY");
771        }
772        else if (node->pn_dval == -INFINITY) {
773          Stream_write_string(f, "Number.NEGATIVE_INFINITY");
774        }
775        else if (node->pn_dval == NAN) {
776          Stream_write_string(f, "Number.NaN");
777        }
778      else {      else {
779        Stream_printf(f, "%.15g", node->pn_dval);        Stream_printf(f, "%.15g", node->pn_dval);
780      }      }
# Line 627  Line 799 
799      }      }
800      break;      break;
801    case TOK_INSTANCEOF:    case TOK_INSTANCEOF:
802      instrument_expression(node->pn_left, f);      output_expression(node->pn_left, f, parenthesize_object_literals);
803      Stream_write_string(f, " instanceof ");      Stream_write_string(f, " instanceof ");
804      instrument_expression(node->pn_right, f);      output_expression(node->pn_right, f, false);
805      break;      break;
806    case TOK_IN:    case TOK_IN:
807      instrument_expression(node->pn_left, f);      output_expression(node->pn_left, f, false);
808      Stream_write_string(f, " in ");      Stream_write_string(f, " in ");
809      instrument_expression(node->pn_right, f);      output_expression(node->pn_right, f, false);
810      break;      break;
811    default:    case TOK_LEXICALSCOPE:
812      fatal("unsupported node type in file %s: %d", file_id, node->pn_type);      assert(node->pn_arity == PN_NAME);
813    }      assert(node->pn_expr->pn_type == TOK_LET);
814  }      assert(node->pn_expr->pn_arity == PN_BINARY);
815        Stream_write_string(f, "let(");
816  static void instrument_var_statement(JSParseNode * node, Stream * f, int indent) {      assert(node->pn_expr->pn_left->pn_type == TOK_LP);
817    assert(node->pn_arity == PN_LIST);      assert(node->pn_expr->pn_left->pn_arity == PN_LIST);
818    Stream_printf(f, "%*s", indent, "");      instrument_declarations(node->pn_expr->pn_left, f);
819    Stream_write_string(f, "var ");      Stream_write_string(f, ") ");
820    for (struct JSParseNode * p = node->pn_u.list.head; p != NULL; p = p->pn_next) {      output_expression(node->pn_expr->pn_right, f, true);
821      assert(p->pn_type == TOK_NAME);      break;
822      assert(p->pn_arity == PN_NAME);    case TOK_YIELD:
823      if (p != node->pn_head) {      assert(node->pn_arity == PN_UNARY);
824        Stream_write_string(f, ", ");      Stream_write_string(f, "yield");
825        if (node->pn_kid != NULL) {
826          Stream_write_char(f, ' ');
827          output_expression(node->pn_kid, f, true);
828      }      }
829      print_string_atom(p->pn_atom, f);      break;
830      if (p->pn_expr != NULL) {    case TOK_ARRAYCOMP:
831        Stream_write_string(f, " = ");      assert(node->pn_arity == PN_LIST);
832        instrument_expression(p->pn_expr, f);      {
833          JSParseNode * block_node;
834          switch (node->pn_count) {
835          case 1:
836            block_node = node->pn_head;
837            break;
838          case 2:
839            block_node = node->pn_head->pn_next;
840            break;
841          default:
842            abort();
843            break;
844          }
845          Stream_write_char(f, '[');
846          output_array_comprehension_or_generator_expression(block_node, f);
847          Stream_write_char(f, ']');
848      }      }
849        break;
850      case TOK_VAR:
851        assert(node->pn_arity == PN_LIST);
852        Stream_write_string(f, "var ");
853        instrument_declarations(node, f);
854        break;
855      case TOK_LET:
856        assert(node->pn_arity == PN_LIST);
857        Stream_write_string(f, "let ");
858        instrument_declarations(node, f);
859        break;
860      default:
861        fatal_source(file_id, node->pn_pos.begin.lineno, "unsupported node type (%d)", node->pn_type);
862    }    }
863  }  }
864    
# Line 663  Line 866 
866    switch (node->pn_type) {    switch (node->pn_type) {
867    case TOK_FUNCTION:    case TOK_FUNCTION:
868      instrument_function(node, f, indent, FUNCTION_NORMAL);      instrument_function(node, f, indent, FUNCTION_NORMAL);
869        Stream_write_char(f, '\n');
870      break;      break;
871    case TOK_LC:    case TOK_LC:
872      assert(node->pn_arity == PN_LIST);      assert(node->pn_arity == PN_LIST);
# Line 684  Line 888 
888      uint16_t line = node->pn_pos.begin.lineno;      uint16_t line = node->pn_pos.begin.lineno;
889      if (! is_jscoverage_if) {      if (! is_jscoverage_if) {
890        if (line > num_lines) {        if (line > num_lines) {
891          fatal("%s: script contains more than 65,535 lines", file_id);          fatal("file %s contains more than 65,535 lines", file_id);
892        }        }
893        if (line >= 2 && exclusive_directives[line - 2]) {        if (line >= 2 && exclusive_directives[line - 2]) {
894          is_jscoverage_if = true;          is_jscoverage_if = true;
# Line 693  Line 897 
897    
898      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
899      Stream_write_string(f, "if (");      Stream_write_string(f, "if (");
900      instrument_expression(node->pn_kid1, f);      output_expression(node->pn_kid1, f, false);
901      Stream_write_string(f, ") {\n");      Stream_write_string(f, ") {\n");
902      if (is_jscoverage_if && node->pn_kid3) {      if (is_jscoverage_if && node->pn_kid3) {
903        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 934 
934      assert(node->pn_arity == PN_BINARY);      assert(node->pn_arity == PN_BINARY);
935      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
936      Stream_write_string(f, "switch (");      Stream_write_string(f, "switch (");
937      instrument_expression(node->pn_left, f);      output_expression(node->pn_left, f, false);
938      Stream_write_string(f, ") {\n");      Stream_write_string(f, ") {\n");
939      for (struct JSParseNode * p = node->pn_right->pn_head; p != NULL; p = p->pn_next) {      {
940        Stream_printf(f, "%*s", indent, "");        JSParseNode * list = node->pn_right;
941        switch (p->pn_type) {        if (list->pn_type == TOK_LEXICALSCOPE) {
942        case TOK_CASE:          list = list->pn_expr;
943          Stream_write_string(f, "case ");        }
944          instrument_expression(p->pn_left, f);        for (struct JSParseNode * p = list->pn_head; p != NULL; p = p->pn_next) {
945          Stream_write_string(f, ":\n");          Stream_printf(f, "%*s", indent, "");
946          break;          switch (p->pn_type) {
947        case TOK_DEFAULT:          case TOK_CASE:
948          Stream_write_string(f, "default:\n");            Stream_write_string(f, "case ");
949          break;            output_expression(p->pn_left, f, false);
950        default:            Stream_write_string(f, ":\n");
951          abort();            break;
952          break;          case TOK_DEFAULT:
953              Stream_write_string(f, "default:\n");
954              break;
955            default:
956              abort();
957              break;
958            }
959            instrument_statement(p->pn_right, f, indent + 2, false);
960        }        }
       instrument_statement(p->pn_right, f, indent + 2, false);  
961      }      }
962      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
963      Stream_write_string(f, "}\n");      Stream_write_string(f, "}\n");
# Line 760  Line 970 
970      assert(node->pn_arity == PN_BINARY);      assert(node->pn_arity == PN_BINARY);
971      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
972      Stream_write_string(f, "while (");      Stream_write_string(f, "while (");
973      instrument_expression(node->pn_left, f);      output_expression(node->pn_left, f, false);
974      Stream_write_string(f, ") {\n");      Stream_write_string(f, ") {\n");
975      instrument_statement(node->pn_right, f, indent + 2, false);      instrument_statement(node->pn_right, f, indent + 2, false);
976      Stream_write_string(f, "}\n");      Stream_write_string(f, "}\n");
# Line 773  Line 983 
983      Stream_write_string(f, "}\n");      Stream_write_string(f, "}\n");
984      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
985      Stream_write_string(f, "while (");      Stream_write_string(f, "while (");
986      instrument_expression(node->pn_right, f);      output_expression(node->pn_right, f, false);
987      Stream_write_string(f, ");\n");      Stream_write_string(f, ");\n");
988      break;      break;
989    case TOK_FOR:    case TOK_FOR:
990      assert(node->pn_arity == PN_BINARY);      assert(node->pn_arity == PN_BINARY);
991      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
     Stream_write_string(f, "for (");  
992      switch (node->pn_left->pn_type) {      switch (node->pn_left->pn_type) {
993      case TOK_IN:      case TOK_IN:
994        /* for/in */        /* for/in */
995        assert(node->pn_left->pn_arity == PN_BINARY);        assert(node->pn_left->pn_arity == PN_BINARY);
996        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);  
997        break;        break;
998      case TOK_RESERVED:      case TOK_RESERVED:
999        /* for (;;) */        /* for (;;) */
1000        assert(node->pn_left->pn_arity == PN_TERNARY);        assert(node->pn_left->pn_arity == PN_TERNARY);
1001          Stream_write_string(f, "for (");
1002        if (node->pn_left->pn_kid1) {        if (node->pn_left->pn_kid1) {
1003          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);  
         }  
1004        }        }
1005        Stream_write_string(f, ";");        Stream_write_string(f, ";");
1006        if (node->pn_left->pn_kid2) {        if (node->pn_left->pn_kid2) {
1007          Stream_write_char(f, ' ');          Stream_write_char(f, ' ');
1008          instrument_expression(node->pn_left->pn_kid2, f);          output_expression(node->pn_left->pn_kid2, f, false);
1009        }        }
1010        Stream_write_string(f, ";");        Stream_write_string(f, ";");
1011        if (node->pn_left->pn_kid3) {        if (node->pn_left->pn_kid3) {
1012          Stream_write_char(f, ' ');          Stream_write_char(f, ' ');
1013          instrument_expression(node->pn_left->pn_kid3, f);          output_expression(node->pn_left->pn_kid3, f, false);
1014        }        }
1015          Stream_write_char(f, ')');
1016        break;        break;
1017      default:      default:
1018        abort();        abort();
1019        break;        break;
1020      }      }
1021      Stream_write_string(f, ") {\n");      Stream_write_string(f, " {\n");
1022      instrument_statement(node->pn_right, f, indent + 2, false);      instrument_statement(node->pn_right, f, indent + 2, false);
1023      Stream_write_string(f, "}\n");      Stream_write_string(f, "}\n");
1024      break;      break;
# Line 839  Line 1026 
1026      assert(node->pn_arity == PN_UNARY);      assert(node->pn_arity == PN_UNARY);
1027      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
1028      Stream_write_string(f, "throw ");      Stream_write_string(f, "throw ");
1029      instrument_expression(node->pn_u.unary.kid, f);      output_expression(node->pn_u.unary.kid, f, false);
1030      Stream_write_string(f, ";\n");      Stream_write_string(f, ";\n");
1031      break;      break;
1032    case TOK_TRY:    case TOK_TRY:
# Line 848  Line 1035 
1035      instrument_statement(node->pn_kid1, f, indent + 2, false);      instrument_statement(node->pn_kid1, f, indent + 2, false);
1036      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
1037      Stream_write_string(f, "}\n");      Stream_write_string(f, "}\n");
1038      {      if (node->pn_kid2) {
1039        for (JSParseNode * catch = node->pn_kid2; catch != NULL; catch = catch->pn_kid2) {        assert(node->pn_kid2->pn_type == TOK_RESERVED);
1040          for (JSParseNode * scope = node->pn_kid2->pn_head; scope != NULL; scope = scope->pn_next) {
1041            assert(scope->pn_type == TOK_LEXICALSCOPE);
1042            JSParseNode * catch = scope->pn_expr;
1043          assert(catch->pn_type == TOK_CATCH);          assert(catch->pn_type == TOK_CATCH);
1044          Stream_printf(f, "%*s", indent, "");          Stream_printf(f, "%*s", indent, "");
1045          Stream_write_string(f, "catch (");          Stream_write_string(f, "catch (");
1046          assert(catch->pn_kid1->pn_arity == PN_NAME);          output_expression(catch->pn_kid1, f, false);
1047          print_string_atom(catch->pn_kid1->pn_atom, f);          if (catch->pn_kid2) {
         if (catch->pn_kid1->pn_expr) {  
1048            Stream_write_string(f, " if ");            Stream_write_string(f, " if ");
1049            instrument_expression(catch->pn_kid1->pn_expr, f);            output_expression(catch->pn_kid2, f, false);
1050          }          }
1051          Stream_write_string(f, ") {\n");          Stream_write_string(f, ") {\n");
1052          instrument_statement(catch->pn_kid3, f, indent + 2, false);          instrument_statement(catch->pn_kid3, f, indent + 2, false);
# Line 892  Line 1081 
1081      assert(node->pn_arity == PN_BINARY);      assert(node->pn_arity == PN_BINARY);
1082      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
1083      Stream_write_string(f, "with (");      Stream_write_string(f, "with (");
1084      instrument_expression(node->pn_left, f);      output_expression(node->pn_left, f, false);
1085      Stream_write_string(f, ") {\n");      Stream_write_string(f, ") {\n");
1086      instrument_statement(node->pn_right, f, indent + 2, false);      instrument_statement(node->pn_right, f, indent + 2, false);
1087      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
1088      Stream_write_string(f, "}\n");      Stream_write_string(f, "}\n");
1089      break;      break;
1090    case TOK_VAR:    case TOK_VAR:
1091      instrument_var_statement(node, f, indent);      Stream_printf(f, "%*s", indent, "");
1092        output_expression(node, f, false);
1093      Stream_write_string(f, ";\n");      Stream_write_string(f, ";\n");
1094      break;      break;
1095    case TOK_RETURN:    case TOK_RETURN:
# Line 908  Line 1098 
1098      Stream_write_string(f, "return");      Stream_write_string(f, "return");
1099      if (node->pn_kid != NULL) {      if (node->pn_kid != NULL) {
1100        Stream_write_char(f, ' ');        Stream_write_char(f, ' ');
1101        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, true);
1102      }      }
1103      Stream_write_string(f, ";\n");      Stream_write_string(f, ";\n");
1104      break;      break;
# Line 916  Line 1106 
1106      assert(node->pn_arity == PN_UNARY);      assert(node->pn_arity == PN_UNARY);
1107      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
1108      if (node->pn_kid != NULL) {      if (node->pn_kid != NULL) {
1109        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, true);
1110      }      }
1111      Stream_write_string(f, ";\n");      Stream_write_string(f, ";\n");
1112      break;      break;
1113    case TOK_COLON:    case TOK_COLON:
1114      {
1115      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 ...  
     */  
1116      Stream_printf(f, "%*s", indent < 2? 0: indent - 2, "");      Stream_printf(f, "%*s", indent < 2? 0: indent - 2, "");
1117      print_string_atom(node->pn_atom, f);      print_string_atom(node->pn_atom, f);
1118      Stream_write_string(f, ":\n");      Stream_write_string(f, ":\n");
1119      /*      JSParseNode * labelled = node->pn_expr;
1120      ... use output_statement instead of instrument_statement.      if (labelled->pn_type == TOK_LEXICALSCOPE) {
1121      */        labelled = labelled->pn_expr;
1122      output_statement(node->pn_expr, f, indent, false);      }
1123        if (labelled->pn_type == TOK_LC) {
1124          /* labelled block */
1125          Stream_printf(f, "%*s", indent, "");
1126          Stream_write_string(f, "{\n");
1127          instrument_statement(labelled, f, indent + 2, false);
1128          Stream_printf(f, "%*s", indent, "");
1129          Stream_write_string(f, "}\n");
1130        }
1131        else {
1132          /*
1133          This one is tricky: can't output instrumentation between the label and the
1134          statement it's supposed to label, so use output_statement instead of
1135          instrument_statement.
1136          */
1137          output_statement(labelled, f, indent, false);
1138        }
1139        break;
1140      }
1141      case TOK_LEXICALSCOPE:
1142        /* let statement */
1143        assert(node->pn_arity == PN_NAME);
1144        switch (node->pn_expr->pn_type) {
1145        case TOK_LET:
1146          /* let statement */
1147          assert(node->pn_expr->pn_arity == PN_BINARY);
1148          instrument_statement(node->pn_expr, f, indent, false);
1149          break;
1150        case TOK_LC:
1151          /* block */
1152          Stream_printf(f, "%*s", indent, "");
1153          Stream_write_string(f, "{\n");
1154          instrument_statement(node->pn_expr, f, indent + 2, false);
1155          Stream_printf(f, "%*s", indent, "");
1156          Stream_write_string(f, "}\n");
1157          break;
1158        case TOK_FOR:
1159          instrument_statement(node->pn_expr, f, indent, false);
1160          break;
1161        default:
1162          abort();
1163          break;
1164        }
1165        break;
1166      case TOK_LET:
1167        switch (node->pn_arity) {
1168        case PN_BINARY:
1169          /* let statement */
1170          Stream_printf(f, "%*s", indent, "");
1171          Stream_write_string(f, "let (");
1172          assert(node->pn_left->pn_type == TOK_LP);
1173          assert(node->pn_left->pn_arity == PN_LIST);
1174          instrument_declarations(node->pn_left, f);
1175          Stream_write_string(f, ") {\n");
1176          instrument_statement(node->pn_right, f, indent + 2, false);
1177          Stream_printf(f, "%*s", indent, "");
1178          Stream_write_string(f, "}\n");
1179          break;
1180        case PN_LIST:
1181          /* let definition */
1182          Stream_printf(f, "%*s", indent, "");
1183          output_expression(node, f, false);
1184          Stream_write_string(f, ";\n");
1185          break;
1186        default:
1187          abort();
1188          break;
1189        }
1190        break;
1191      case TOK_DEBUGGER:
1192        Stream_printf(f, "%*s", indent, "");
1193        Stream_write_string(f, "debugger;\n");
1194      break;      break;
1195    default:    default:
1196      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);
1197    }    }
1198  }  }
1199    
# Line 945  Line 1203 
1203  TOK_EXPORT, TOK_IMPORT are not handled.  TOK_EXPORT, TOK_IMPORT are not handled.
1204  */  */
1205  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) {
1206    if (node->pn_type != TOK_LC) {    if (node->pn_type != TOK_LC && node->pn_type != TOK_LEXICALSCOPE) {
1207      uint16_t line = node->pn_pos.begin.lineno;      uint16_t line = node->pn_pos.begin.lineno;
1208      if (line > num_lines) {      if (line > num_lines) {
1209        fatal("%s: script contains more than 65,535 lines", file_id);        fatal("file %s contains more than 65,535 lines", file_id);
1210      }      }
1211    
1212      /* the root node has line number 0 */      /* the root node has line number 0 */
# Line 996  Line 1254 
1254  }  }
1255    
1256  static void error_reporter(JSContext * context, const char * message, JSErrorReport * report) {  static void error_reporter(JSContext * context, const char * message, JSErrorReport * report) {
1257    fprintf(stderr, "jscoverage: parse error: line %u: %s\n", report->lineno, message);    warn_source(file_id, report->lineno, "%s", message);
1258  }  }
1259    
1260  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) {
1261    file_id = id;    file_id = id;
1262    
   /* 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);  
   }  
   
1263    /* parse the javascript */    /* parse the javascript */
1264      JSParseContext parse_context;
1265      if (! js_InitParseContext(context, &parse_context, NULL, NULL, characters, num_characters, NULL, NULL, 1)) {
1266        fatal("cannot create token stream from file %s", file_id);
1267      }
1268    JSErrorReporter old_error_reporter = JS_SetErrorReporter(context, error_reporter);    JSErrorReporter old_error_reporter = JS_SetErrorReporter(context, error_reporter);
1269    JSParseNode * node = js_ParseTokenStream(context, global, token_stream);    JSParseNode * node = js_ParseScript(context, global, &parse_context);
1270    if (node == NULL) {    if (node == NULL) {
1271      js_ReportUncaughtException(context);      js_ReportUncaughtException(context);
1272      fatal("parse error in file: %s", file_id);      fatal("parse error in file %s", file_id);
1273    }    }
1274    JS_SetErrorReporter(context, old_error_reporter);    JS_SetErrorReporter(context, old_error_reporter);
1275    num_lines = node->pn_pos.end.lineno;    num_lines = node->pn_pos.end.lineno;
# Line 1034  Line 1290 
1290    size_t i = 0;    size_t i = 0;
1291    while (i < num_characters) {    while (i < num_characters) {
1292      if (line_number == UINT16_MAX) {      if (line_number == UINT16_MAX) {
1293        fatal("%s: script has more than 65,535 lines", file_id);        fatal("file %s contains more than 65,535 lines", file_id);
1294      }      }
1295      line_number++;      line_number++;
1296      size_t line_start = i;      size_t line_start = i;
# Line 1097  Line 1353 
1353    
1354    Stream * instrumented = Stream_new(0);    Stream * instrumented = Stream_new(0);
1355    instrument_statement(node, instrumented, 0, false);    instrument_statement(node, instrumented, 0, false);
1356      js_FinishParseContext(context, &parse_context);
1357    
1358    /* write line number info to the output */    /* write line number info to the output */
1359    Stream_write_string(output, "/* automatically generated by JSCoverage - do not edit */\n");    Stream_write_string(output, "/* automatically generated by JSCoverage - do not edit */\n");
1360    Stream_write_string(output, "if (! top._$jscoverage) {\n  top._$jscoverage = {};\n}\n");    if (jscoverage_mozilla) {
1361    Stream_write_string(output, "var _$jscoverage = top._$jscoverage;\n");      Stream_write_string(output, "try {\n");
1362        Stream_write_string(output, "  Components.utils.import('resource://gre/modules/jscoverage.jsm');\n");
1363        Stream_printf(output, "  dump('%s: successfully imported jscoverage module\\n');\n", id);
1364        Stream_write_string(output, "}\n");
1365        Stream_write_string(output, "catch (e) {\n");
1366        Stream_write_string(output, "  _$jscoverage = {};\n");
1367        Stream_printf(output, "  dump('%s: failed to import jscoverage module - coverage not available for this file\\n');\n", id);
1368        Stream_write_string(output, "}\n");
1369      }
1370      else {
1371        Stream_write_string(output, "if (! top._$jscoverage) {\n  top._$jscoverage = {};\n}\n");
1372        Stream_write_string(output, "var _$jscoverage = top._$jscoverage;\n");
1373      }
1374    Stream_printf(output, "if (! _$jscoverage['%s']) {\n", file_id);    Stream_printf(output, "if (! _$jscoverage['%s']) {\n", file_id);
1375    Stream_printf(output, "  _$jscoverage['%s'] = [];\n", file_id);    Stream_printf(output, "  _$jscoverage['%s'] = [];\n", file_id);
1376    for (int i = 0; i < num_lines; i++) {    for (int i = 0; i < num_lines; i++) {
# Line 1177  Line 1446 
1446            /* line feed (new line) */            /* line feed (new line) */
1447            done = true;            done = true;
1448            break;            break;
1449            /* IE doesn't support this */
1450            /*
1451          case 0xb:          case 0xb:
           /* vertical tab */  
1452            Stream_write_string(output, "\\v");            Stream_write_string(output, "\\v");
1453            break;            break;
1454            */
1455          case 0xc:          case 0xc:
1456            /* form feed */            /* form feed */
1457            Stream_write_string(output, "\\f");            Stream_write_string(output, "\\f");
# Line 1235  Line 1506 
1506            /* line feed (new line) */            /* line feed (new line) */
1507            done = true;            done = true;
1508            break;            break;
1509            /* IE doesn't support this */
1510            /*
1511          case 0xb:          case 0xb:
           /* vertical tab */  
1512            Stream_write_string(output, "\\v");            Stream_write_string(output, "\\v");
1513            break;            break;
1514            */
1515          case 0xc:          case 0xc:
1516            /* form feed */            /* form feed */
1517            Stream_write_string(output, "\\f");            Stream_write_string(output, "\\f");
# Line 1365  Line 1638 
1638  }  }
1639    
1640  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) {
1641      int result = 0;
1642    
1643    jschar * base = js_InflateString(context, (char *) json, &length);    jschar * base = js_InflateString(context, (char *) json, &length);
1644    if (base == NULL) {    if (base == NULL) {
1645      fatal("out of memory");      fatal("out of memory");
# Line 1377  Line 1652 
1652    
1653    JS_free(context, base);    JS_free(context, base);
1654    
1655    JSTokenStream * token_stream = js_NewTokenStream(context, parenthesized_json, length + 2, NULL, 1, NULL);    JSParseContext parse_context;
1656    if (token_stream == NULL) {    if (! js_InitParseContext(context, &parse_context, NULL, NULL, parenthesized_json, length + 2, NULL, NULL, 1)) {
1657      fatal("cannot create token stream");      free(parenthesized_json);
1658        return -1;
1659    }    }
1660      JSParseNode * root = js_ParseScript(context, global, &parse_context);
   JSParseNode * root = js_ParseTokenStream(context, global, token_stream);  
1661    free(parenthesized_json);    free(parenthesized_json);
1662    if (root == NULL) {    if (root == NULL) {
1663      return -1;      result = -1;
1664        goto done;
1665    }    }
1666    
1667    /* root node must be TOK_LC */    /* root node must be TOK_LC */
1668    if (root->pn_type != TOK_LC) {    if (root->pn_type != TOK_LC) {
1669      return -1;      result = -1;
1670        goto done;
1671    }    }
1672    JSParseNode * semi = root->pn_u.list.head;    JSParseNode * semi = root->pn_u.list.head;
1673    
1674    /* 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 */
1675    if (semi->pn_type != TOK_SEMI || semi->pn_next != NULL) {    if (semi->pn_type != TOK_SEMI || semi->pn_next != NULL) {
1676      return -1;      result = -1;
1677        goto done;
1678    }    }
1679    JSParseNode * parenthesized = semi->pn_kid;    JSParseNode * parenthesized = semi->pn_kid;
1680    
1681    /* this must be a parenthesized expression */    /* this must be a parenthesized expression */
1682    if (parenthesized->pn_type != TOK_RP) {    if (parenthesized->pn_type != TOK_RP) {
1683      return -1;      result = -1;
1684        goto done;
1685    }    }
1686    JSParseNode * object = parenthesized->pn_kid;    JSParseNode * object = parenthesized->pn_kid;
1687    
1688    /* this must be an object literal */    /* this must be an object literal */
1689    if (object->pn_type != TOK_RC) {    if (object->pn_type != TOK_RC) {
1690      return -1;      result = -1;
1691        goto done;
1692    }    }
1693    
1694    for (JSParseNode * p = object->pn_head; p != NULL; p = p->pn_next) {    for (JSParseNode * p = object->pn_head; p != NULL; p = p->pn_next) {
1695      /* every element of this list must be TOK_COLON */      /* every element of this list must be TOK_COLON */
1696      if (p->pn_type != TOK_COLON) {      if (p->pn_type != TOK_COLON) {
1697        return -1;        result = -1;
1698          goto done;
1699      }      }
1700    
1701      /* the key must be a string representing the file */      /* the key must be a string representing the file */
1702      JSParseNode * key = p->pn_left;      JSParseNode * key = p->pn_left;
1703      if (key->pn_type != TOK_STRING || ! ATOM_IS_STRING(key->pn_atom)) {      if (key->pn_type != TOK_STRING || ! ATOM_IS_STRING(key->pn_atom)) {
1704        return -1;        result = -1;
1705          goto done;
1706      }      }
1707      char * id_bytes = JS_GetStringBytes(ATOM_TO_STRING(key->pn_atom));      char * id_bytes = JS_GetStringBytes(ATOM_TO_STRING(key->pn_atom));
1708    
1709      /* the value must be an object literal OR an array */      /* the value must be an object literal OR an array */
1710      JSParseNode * value = p->pn_right;      JSParseNode * value = p->pn_right;
1711      if (! (value->pn_type == TOK_RC || value->pn_type == TOK_RB)) {      if (! (value->pn_type == TOK_RC || value->pn_type == TOK_RB)) {
1712        return -1;        result = -1;
1713          goto done;
1714      }      }
1715    
1716      JSParseNode * array = NULL;      JSParseNode * array = NULL;
# Line 1439  Line 1722 
1722      else if (value->pn_type == TOK_RC) {      else if (value->pn_type == TOK_RC) {
1723        /* an object literal */        /* an object literal */
1724        if (value->pn_count != 2) {        if (value->pn_count != 2) {
1725          return -1;          result = -1;
1726            goto done;
1727        }        }
1728        for (JSParseNode * element = value->pn_head; element != NULL; element = element->pn_next) {        for (JSParseNode * element = value->pn_head; element != NULL; element = element->pn_next) {
1729          if (element->pn_type != TOK_COLON) {          if (element->pn_type != TOK_COLON) {
1730            return -1;            result = -1;
1731              goto done;
1732          }          }
1733          JSParseNode * left = element->pn_left;          JSParseNode * left = element->pn_left;
1734          if (left->pn_type != TOK_STRING || ! ATOM_IS_STRING(left->pn_atom)) {          if (left->pn_type != TOK_STRING || ! ATOM_IS_STRING(left->pn_atom)) {
1735            return -1;            result = -1;
1736              goto done;
1737          }          }
1738          const char * s = JS_GetStringBytes(ATOM_TO_STRING(left->pn_atom));          const char * s = JS_GetStringBytes(ATOM_TO_STRING(left->pn_atom));
1739          if (strcmp(s, "coverage") == 0) {          if (strcmp(s, "coverage") == 0) {
1740            array = element->pn_right;            array = element->pn_right;
1741            if (array->pn_type != TOK_RB) {            if (array->pn_type != TOK_RB) {
1742              return -1;              result = -1;
1743                goto done;
1744            }            }
1745          }          }
1746          else if (strcmp(s, "source") == 0) {          else if (strcmp(s, "source") == 0) {
1747            source = element->pn_right;            source = element->pn_right;
1748            if (source->pn_type != TOK_RB) {            if (source->pn_type != TOK_RB) {
1749              return -1;              result = -1;
1750                goto done;
1751            }            }
1752          }          }
1753          else {          else {
1754            return -1;            result = -1;
1755              goto done;
1756          }          }
1757        }        }
1758      }      }
1759      else {      else {
1760        return -1;        result = -1;
1761          goto done;
1762      }      }
1763    
1764      if (array == NULL) {      if (array == NULL) {
1765        return -1;        result = -1;
1766          goto done;
1767      }      }
1768    
1769      /* look up the file in the coverage table */      /* look up the file in the coverage table */
# Line 1496  Line 1787 
1787            file_coverage->coverage_lines[i] = -1;            file_coverage->coverage_lines[i] = -1;
1788          }          }
1789          else {          else {
1790            return -1;            result = -1;
1791              goto done;
1792          }          }
1793        }        }
1794        assert(i == array->pn_count);        assert(i == array->pn_count);
# Line 1512  Line 1804 
1804        /* sanity check */        /* sanity check */
1805        assert(strcmp(file_coverage->id, id_bytes) == 0);        assert(strcmp(file_coverage->id, id_bytes) == 0);
1806        if (file_coverage->num_coverage_lines != array->pn_count) {        if (file_coverage->num_coverage_lines != array->pn_count) {
1807          return -2;          result = -2;
1808            goto done;
1809        }        }
1810    
1811        /* merge the coverage */        /* merge the coverage */
# Line 1520  Line 1813 
1813        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++) {
1814          if (element->pn_type == TOK_NUMBER) {          if (element->pn_type == TOK_NUMBER) {
1815            if (file_coverage->coverage_lines[i] == -1) {            if (file_coverage->coverage_lines[i] == -1) {
1816              return -2;              result = -2;
1817                goto done;
1818            }            }
1819            file_coverage->coverage_lines[i] += (int) element->pn_dval;            file_coverage->coverage_lines[i] += (int) element->pn_dval;
1820          }          }
1821          else if (element->pn_type == TOK_PRIMARY && element->pn_op == JSOP_NULL) {          else if (element->pn_type == TOK_PRIMARY && element->pn_op == JSOP_NULL) {
1822            if (file_coverage->coverage_lines[i] != -1) {            if (file_coverage->coverage_lines[i] != -1) {
1823              return -2;              result = -2;
1824                goto done;
1825            }            }
1826          }          }
1827          else {          else {
1828            return -1;            result = -1;
1829              goto done;
1830          }          }
1831        }        }
1832        assert(i == array->pn_count);        assert(i == array->pn_count);
# Line 1543  Line 1839 
1839        uint32 i = 0;        uint32 i = 0;
1840        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++) {
1841          if (element->pn_type != TOK_STRING) {          if (element->pn_type != TOK_STRING) {
1842            return -1;            result = -1;
1843              goto done;
1844          }          }
1845          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)));
1846        }        }
# Line 1551  Line 1848 
1848      }      }
1849    }    }
1850    
1851    return 0;  done:
1852      js_FinishParseContext(context, &parse_context);
1853      return result;
1854  }  }

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

  ViewVC Help
Powered by ViewVC 1.1.24