/[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 240 by siliconforks, Fri Oct 3 23:51:32 2008 UTC revision 387 by siliconforks, Thu Oct 30 03:14:12 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>
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 48  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 62  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 73  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 88  Line 110 
110    JS_DestroyRuntime(runtime);    JS_DestroyRuntime(runtime);
111  }  }
112    
113    static void print_javascript(const jschar * characters, size_t num_characters, Stream * f) {
114      for (size_t i = 0; i < num_characters; i++) {
115        jschar c = characters[i];
116        /*
117        XXX does not handle no-break space, other unicode "space separator"
118        */
119        switch (c) {
120        case 0x9:
121        case 0xB:
122        case 0xC:
123          Stream_write_char(f, c);
124          break;
125        default:
126          if (32 <= c && c <= 126) {
127            Stream_write_char(f, c);
128          }
129          else {
130            Stream_printf(f, "\\u%04x", c);
131          }
132          break;
133        }
134      }
135    }
136    
137  static void print_string(JSString * s, Stream * f) {  static void print_string(JSString * s, Stream * f) {
138    size_t length = JSSTRING_LENGTH(s);    size_t length = JSSTRING_LENGTH(s);
139    jschar * characters = JSSTRING_CHARS(s);    jschar * characters = JSSTRING_CHARS(s);
# Line 122  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 171  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 181  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 214  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_char(f, '}');
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      }
437      output_expression(function_node, f, false);
438    Stream_write_char(f, '(');    Stream_write_char(f, '(');
439    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) {
440      if (p != node->pn_head->pn_next) {      if (p != node->pn_head->pn_next) {
441        Stream_write_string(f, ", ");        Stream_write_string(f, ", ");
442      }      }
443      instrument_expression(p, f);      output_expression(p, f, false);
444    }    }
445    Stream_write_char(f, ')');    Stream_write_char(f, ')');
446  }  }
447    
448    static void instrument_declarations(JSParseNode * list, Stream * f) {
449      assert(list->pn_arity == PN_LIST);
450      for (JSParseNode * p = list->pn_head; p != NULL; p = p->pn_next) {
451        if (p != list->pn_head) {
452          Stream_write_string(f, ", ");
453        }
454        output_expression(p, f, false);
455      }
456    }
457    
458  /*  /*
459  See <Expressions> in jsparse.h.  See <Expressions> in jsparse.h.
460  TOK_FUNCTION is handled as a statement and as an expression.  TOK_FUNCTION is handled as a statement and as an expression.
# Line 301  Line 468 
468  TOK_INSTANCEOF  binary  TOK_INSTANCEOF  binary
469  TOK_IN          binary  TOK_IN          binary
470  */  */
471  static void instrument_expression(JSParseNode * node, Stream * f) {  static void output_expression(JSParseNode * node, Stream * f, bool parenthesize_object_literals) {
472    switch (node->pn_type) {    switch (node->pn_type) {
473    case TOK_FUNCTION:    case TOK_FUNCTION:
474        Stream_write_char(f, '(');
475      instrument_function(node, f, 0, FUNCTION_NORMAL);      instrument_function(node, f, 0, FUNCTION_NORMAL);
476        Stream_write_char(f, ')');
477      break;      break;
478    case TOK_COMMA:    case TOK_COMMA:
479      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) {
480        if (p != node->pn_head) {        if (p != node->pn_head) {
481          Stream_write_string(f, ", ");          Stream_write_string(f, ", ");
482        }        }
483        instrument_expression(p, f);        output_expression(p, f, parenthesize_object_literals);
484      }      }
485      break;      break;
486    case TOK_ASSIGN:    case TOK_ASSIGN:
487      instrument_expression(node->pn_left, f);      output_expression(node->pn_left, f, parenthesize_object_literals);
488      Stream_write_char(f, ' ');      Stream_write_char(f, ' ');
489      switch (node->pn_op) {      switch (node->pn_op) {
490      case JSOP_ADD:      case JSOP_ADD:
# Line 336  Line 505 
505        break;        break;
506      }      }
507      Stream_write_string(f, "= ");      Stream_write_string(f, "= ");
508      instrument_expression(node->pn_right, f);      output_expression(node->pn_right, f, false);
509      break;      break;
510    case TOK_HOOK:    case TOK_HOOK:
511      instrument_expression(node->pn_kid1, f);      output_expression(node->pn_kid1, f, parenthesize_object_literals);
512      Stream_write_string(f, "? ");      Stream_write_string(f, "? ");
513      instrument_expression(node->pn_kid2, f);      output_expression(node->pn_kid2, f, false);
514      Stream_write_string(f, ": ");      Stream_write_string(f, ": ");
515      instrument_expression(node->pn_kid3, f);      output_expression(node->pn_kid3, f, false);
516      break;      break;
517    case TOK_OR:    case TOK_OR:
     instrument_expression(node->pn_left, f);  
     Stream_write_string(f, " || ");  
     instrument_expression(node->pn_right, f);  
     break;  
518    case TOK_AND:    case TOK_AND:
     instrument_expression(node->pn_left, f);  
     Stream_write_string(f, " && ");  
     instrument_expression(node->pn_right, f);  
     break;  
519    case TOK_BITOR:    case TOK_BITOR:
520    case TOK_BITXOR:    case TOK_BITXOR:
521    case TOK_BITAND:    case TOK_BITAND:
# Line 367  Line 528 
528    case TOK_DIVOP:    case TOK_DIVOP:
529      switch (node->pn_arity) {      switch (node->pn_arity) {
530      case PN_BINARY:      case PN_BINARY:
531        instrument_expression(node->pn_left, f);        output_expression(node->pn_left, f, parenthesize_object_literals);
532        Stream_printf(f, " %s ", get_op(node->pn_op));        Stream_printf(f, " %s ", get_op(node->pn_op));
533        instrument_expression(node->pn_right, f);        output_expression(node->pn_right, f, false);
534        break;        break;
535      case PN_LIST:      case PN_LIST:
536        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) {
537          if (p != node->pn_head) {          if (p == node->pn_head) {
538              output_expression(p, f, parenthesize_object_literals);
539            }
540            else {
541            Stream_printf(f, " %s ", get_op(node->pn_op));            Stream_printf(f, " %s ", get_op(node->pn_op));
542              output_expression(p, f, false);
543          }          }
         instrument_expression(p, f);  
544        }        }
545        break;        break;
546      default:      default:
# Line 386  Line 550 
550    case TOK_UNARYOP:    case TOK_UNARYOP:
551      switch (node->pn_op) {      switch (node->pn_op) {
552      case JSOP_NEG:      case JSOP_NEG:
553        Stream_write_char(f, '-');        Stream_write_string(f, "- ");
554        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, false);
555        break;        break;
556      case JSOP_POS:      case JSOP_POS:
557        Stream_write_char(f, '+');        Stream_write_string(f, "+ ");
558        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, false);
559        break;        break;
560      case JSOP_NOT:      case JSOP_NOT:
561        Stream_write_char(f, '!');        Stream_write_string(f, "! ");
562        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, false);
563        break;        break;
564      case JSOP_BITNOT:      case JSOP_BITNOT:
565        Stream_write_char(f, '~');        Stream_write_string(f, "~ ");
566        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, false);
567        break;        break;
568      case JSOP_TYPEOF:      case JSOP_TYPEOF:
569        Stream_write_string(f, "typeof ");        Stream_write_string(f, "typeof ");
570        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, false);
571        break;        break;
572      case JSOP_VOID:      case JSOP_VOID:
573        Stream_write_string(f, "void ");        Stream_write_string(f, "void ");
574        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, false);
575        break;        break;
576      default:      default:
577        abort();        fatal_source(file_id, node->pn_pos.begin.lineno, "unknown operator (%d)", node->pn_op);
578        break;        break;
579      }      }
580      break;      break;
# Line 424  Line 588 
588      case JSOP_INCPROP:      case JSOP_INCPROP:
589      case JSOP_INCELEM:      case JSOP_INCELEM:
590        Stream_write_string(f, "++");        Stream_write_string(f, "++");
591        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, false);
592        break;        break;
593      case JSOP_DECNAME:      case JSOP_DECNAME:
594      case JSOP_DECPROP:      case JSOP_DECPROP:
595      case JSOP_DECELEM:      case JSOP_DECELEM:
596        Stream_write_string(f, "--");        Stream_write_string(f, "--");
597        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, false);
598        break;        break;
599      case JSOP_NAMEINC:      case JSOP_NAMEINC:
600      case JSOP_PROPINC:      case JSOP_PROPINC:
601      case JSOP_ELEMINC:      case JSOP_ELEMINC:
602        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, parenthesize_object_literals);
603        Stream_write_string(f, "++");        Stream_write_string(f, "++");
604        break;        break;
605      case JSOP_NAMEDEC:      case JSOP_NAMEDEC:
606      case JSOP_PROPDEC:      case JSOP_PROPDEC:
607      case JSOP_ELEMDEC:      case JSOP_ELEMDEC:
608        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, parenthesize_object_literals);
609        Stream_write_string(f, "--");        Stream_write_string(f, "--");
610        break;        break;
611      default:      default:
# Line 455  Line 619 
619      break;      break;
620    case TOK_DELETE:    case TOK_DELETE:
621      Stream_write_string(f, "delete ");      Stream_write_string(f, "delete ");
622      instrument_expression(node->pn_kid, f);      output_expression(node->pn_kid, f, false);
623      break;      break;
624    case TOK_DOT:    case TOK_DOT:
625        /* numeric literals must be parenthesized */
626        switch (node->pn_expr->pn_type) {
627        case TOK_NUMBER:
628          Stream_write_char(f, '(');
629          output_expression(node->pn_expr, f, false);
630          Stream_write_char(f, ')');
631          break;
632        default:
633          output_expression(node->pn_expr, f, true);
634          break;
635        }
636      /*      /*
637      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'
638      contains illegal characters, we have to use the subscript syntax instead of      contains illegal characters, we have to use the subscript syntax instead of
639      the dot syntax.      the dot syntax.
640      */      */
     instrument_expression(node->pn_expr, f);  
641      assert(ATOM_IS_STRING(node->pn_atom));      assert(ATOM_IS_STRING(node->pn_atom));
642      {      {
643        JSString * s = ATOM_TO_STRING(node->pn_atom);        JSString * s = ATOM_TO_STRING(node->pn_atom);
644        /* XXX - semantics changed in 1.7 */        bool must_quote;
645        if (! ATOM_KEYWORD(node->pn_atom) && js_IsIdentifier(s)) {        if (JSSTRING_LENGTH(s) == 0) {
646          Stream_write_char(f, '.');          must_quote = true;
647          print_string_atom(node->pn_atom, f);        }
648          else if (js_CheckKeyword(JSSTRING_CHARS(s), JSSTRING_LENGTH(s)) != TOK_EOF) {
649            must_quote = true;
650          }
651          else if (! js_IsIdentifier(s)) {
652            must_quote = true;
653        }        }
654        else {        else {
655            must_quote = false;
656          }
657          if (must_quote) {
658          Stream_write_char(f, '[');          Stream_write_char(f, '[');
659          print_quoted_string_atom(node->pn_atom, f);          print_quoted_string_atom(node->pn_atom, f);
660          Stream_write_char(f, ']');          Stream_write_char(f, ']');
661        }        }
662          else {
663            Stream_write_char(f, '.');
664            print_string_atom(node->pn_atom, f);
665          }
666      }      }
667      break;      break;
668    case TOK_LB:    case TOK_LB:
669      instrument_expression(node->pn_left, f);      output_expression(node->pn_left, f, false);
670      Stream_write_char(f, '[');      Stream_write_char(f, '[');
671      instrument_expression(node->pn_right, f);      output_expression(node->pn_right, f, false);
672      Stream_write_char(f, ']');      Stream_write_char(f, ']');
673      break;      break;
674    case TOK_LP:    case TOK_LP:
# Line 496  Line 682 
682        }        }
683        /* TOK_COMMA is a special case: a hole in the array */        /* TOK_COMMA is a special case: a hole in the array */
684        if (p->pn_type != TOK_COMMA) {        if (p->pn_type != TOK_COMMA) {
685          instrument_expression(p, f);          output_expression(p, f, false);
686        }        }
687      }      }
688      if (node->pn_extra == PNX_ENDCOMMA) {      if (node->pn_extra == PNX_ENDCOMMA) {
# Line 505  Line 691 
691      Stream_write_char(f, ']');      Stream_write_char(f, ']');
692      break;      break;
693    case TOK_RC:    case TOK_RC:
694        if (parenthesize_object_literals) {
695          Stream_write_char(f, '(');
696        }
697      Stream_write_char(f, '{');      Stream_write_char(f, '{');
698      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) {
699        assert(p->pn_type == TOK_COLON);        if (p->pn_type != TOK_COLON) {
700            fatal_source(file_id, p->pn_pos.begin.lineno, "unsupported node type (%d)", p->pn_type);
701          }
702        if (p != node->pn_head) {        if (p != node->pn_head) {
703          Stream_write_string(f, ", ");          Stream_write_string(f, ", ");
704        }        }
# Line 515  Line 706 
706        /* check whether this is a getter or setter */        /* check whether this is a getter or setter */
707        switch (p->pn_op) {        switch (p->pn_op) {
708        case JSOP_GETTER:        case JSOP_GETTER:
         Stream_write_string(f, "get ");  
         instrument_expression(p->pn_left, f);  
         instrument_function(p->pn_right, f, 0, FUNCTION_GETTER_OR_SETTER);  
         break;  
709        case JSOP_SETTER:        case JSOP_SETTER:
710          Stream_write_string(f, "set ");          if (p->pn_op == JSOP_GETTER) {
711          instrument_expression(p->pn_left, f);            Stream_write_string(f, "get ");
712            }
713            else {
714              Stream_write_string(f, "set ");
715            }
716            output_expression(p->pn_left, f, false);
717            if (p->pn_right->pn_type != TOK_FUNCTION) {
718              fatal_source(file_id, p->pn_pos.begin.lineno, "expected function");
719            }
720          instrument_function(p->pn_right, f, 0, FUNCTION_GETTER_OR_SETTER);          instrument_function(p->pn_right, f, 0, FUNCTION_GETTER_OR_SETTER);
721          break;          break;
722        default:        default:
723          instrument_expression(p->pn_left, f);          output_expression(p->pn_left, f, false);
724          Stream_write_string(f, ": ");          Stream_write_string(f, ": ");
725          instrument_expression(p->pn_right, f);          output_expression(p->pn_right, f, false);
726          break;          break;
727        }        }
728      }      }
729      Stream_write_char(f, '}');      Stream_write_char(f, '}');
730        if (parenthesize_object_literals) {
731          Stream_write_char(f, ')');
732        }
733      break;      break;
734    case TOK_RP:    case TOK_RP:
735      Stream_write_char(f, '(');      Stream_write_char(f, '(');
736      instrument_expression(node->pn_kid, f);      output_expression(node->pn_kid, f, false);
737      Stream_write_char(f, ')');      Stream_write_char(f, ')');
738      break;      break;
739    case TOK_NAME:    case TOK_NAME:
740      print_string_atom(node->pn_atom, f);      print_string_atom(node->pn_atom, f);
741        if (node->pn_expr != NULL) {
742          Stream_write_string(f, " = ");
743          output_expression(node->pn_expr, f, false);
744        }
745      break;      break;
746    case TOK_STRING:    case TOK_STRING:
747      print_quoted_string_atom(node->pn_atom, f);      print_quoted_string_atom(node->pn_atom, f);
748      break;      break;
749    case TOK_OBJECT:    case TOK_REGEXP:
750      switch (node->pn_op) {      assert(node->pn_op == JSOP_REGEXP);
751      case JSOP_OBJECT:      {
752        /* I assume this is JSOP_REGEXP */        JSObject * object = node->pn_pob->object;
753        abort();        jsval result;
754        break;        js_regexp_toString(context, object, &result);
755      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;  
756      }      }
757      break;      break;
758    case TOK_NUMBER:    case TOK_NUMBER:
# Line 598  Line 789 
789      }      }
790      break;      break;
791    case TOK_INSTANCEOF:    case TOK_INSTANCEOF:
792      instrument_expression(node->pn_left, f);      output_expression(node->pn_left, f, parenthesize_object_literals);
793      Stream_write_string(f, " instanceof ");      Stream_write_string(f, " instanceof ");
794      instrument_expression(node->pn_right, f);      output_expression(node->pn_right, f, false);
795      break;      break;
796    case TOK_IN:    case TOK_IN:
797      instrument_expression(node->pn_left, f);      output_expression(node->pn_left, f, false);
798      Stream_write_string(f, " in ");      Stream_write_string(f, " in ");
799      instrument_expression(node->pn_right, f);      output_expression(node->pn_right, f, false);
800      break;      break;
801    default:    case TOK_LEXICALSCOPE:
802      fatal("unsupported node type in file %s: %d", file_id, node->pn_type);      assert(node->pn_arity == PN_NAME);
803    }      assert(node->pn_expr->pn_type == TOK_LET);
804  }      assert(node->pn_expr->pn_arity == PN_BINARY);
805        Stream_write_string(f, "let(");
806  static void instrument_var_statement(JSParseNode * node, Stream * f, int indent) {      assert(node->pn_expr->pn_left->pn_type == TOK_LP);
807    assert(node->pn_arity == PN_LIST);      assert(node->pn_expr->pn_left->pn_arity == PN_LIST);
808    Stream_printf(f, "%*s", indent, "");      instrument_declarations(node->pn_expr->pn_left, f);
809    Stream_write_string(f, "var ");      Stream_write_string(f, ") ");
810    for (struct JSParseNode * p = node->pn_u.list.head; p != NULL; p = p->pn_next) {      output_expression(node->pn_expr->pn_right, f, true);
811      assert(p->pn_type == TOK_NAME);      break;
812      assert(p->pn_arity == PN_NAME);    case TOK_YIELD:
813      if (p != node->pn_head) {      assert(node->pn_arity == PN_UNARY);
814        Stream_write_string(f, ", ");      Stream_write_string(f, "yield");
815        if (node->pn_kid != NULL) {
816          Stream_write_char(f, ' ');
817          output_expression(node->pn_kid, f, true);
818      }      }
819      print_string_atom(p->pn_atom, f);      break;
820      if (p->pn_expr != NULL) {    case TOK_ARRAYCOMP:
821        Stream_write_string(f, " = ");      assert(node->pn_arity == PN_LIST);
822        instrument_expression(p->pn_expr, f);      {
823          JSParseNode * block_node;
824          switch (node->pn_count) {
825          case 1:
826            block_node = node->pn_head;
827            break;
828          case 2:
829            block_node = node->pn_head->pn_next;
830            break;
831          default:
832            abort();
833            break;
834          }
835          Stream_write_char(f, '[');
836          output_array_comprehension_or_generator_expression(block_node, f);
837          Stream_write_char(f, ']');
838      }      }
839        break;
840      case TOK_VAR:
841        assert(node->pn_arity == PN_LIST);
842        Stream_write_string(f, "var ");
843        instrument_declarations(node, f);
844        break;
845      case TOK_LET:
846        assert(node->pn_arity == PN_LIST);
847        Stream_write_string(f, "let ");
848        instrument_declarations(node, f);
849        break;
850      default:
851        fatal_source(file_id, node->pn_pos.begin.lineno, "unsupported node type (%d)", node->pn_type);
852    }    }
853  }  }
854    
# Line 634  Line 856 
856    switch (node->pn_type) {    switch (node->pn_type) {
857    case TOK_FUNCTION:    case TOK_FUNCTION:
858      instrument_function(node, f, indent, FUNCTION_NORMAL);      instrument_function(node, f, indent, FUNCTION_NORMAL);
859        Stream_write_char(f, '\n');
860      break;      break;
861    case TOK_LC:    case TOK_LC:
862      assert(node->pn_arity == PN_LIST);      assert(node->pn_arity == PN_LIST);
# Line 655  Line 878 
878      uint16_t line = node->pn_pos.begin.lineno;      uint16_t line = node->pn_pos.begin.lineno;
879      if (! is_jscoverage_if) {      if (! is_jscoverage_if) {
880        if (line > num_lines) {        if (line > num_lines) {
881          fatal("%s: script contains more than 65,535 lines", file_id);          fatal("file %s contains more than 65,535 lines", file_id);
882        }        }
883        if (line >= 2 && exclusive_directives[line - 2]) {        if (line >= 2 && exclusive_directives[line - 2]) {
884          is_jscoverage_if = true;          is_jscoverage_if = true;
# Line 664  Line 887 
887    
888      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
889      Stream_write_string(f, "if (");      Stream_write_string(f, "if (");
890      instrument_expression(node->pn_kid1, f);      output_expression(node->pn_kid1, f, false);
891      Stream_write_string(f, ") {\n");      Stream_write_string(f, ") {\n");
892      if (is_jscoverage_if && node->pn_kid3) {      if (is_jscoverage_if && node->pn_kid3) {
893        uint16_t else_start = node->pn_kid3->pn_pos.begin.lineno;        uint16_t else_start = node->pn_kid3->pn_pos.begin.lineno;
# Line 701  Line 924 
924      assert(node->pn_arity == PN_BINARY);      assert(node->pn_arity == PN_BINARY);
925      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
926      Stream_write_string(f, "switch (");      Stream_write_string(f, "switch (");
927      instrument_expression(node->pn_left, f);      output_expression(node->pn_left, f, false);
928      Stream_write_string(f, ") {\n");      Stream_write_string(f, ") {\n");
929      for (struct JSParseNode * p = node->pn_right->pn_head; p != NULL; p = p->pn_next) {      {
930        Stream_printf(f, "%*s", indent, "");        JSParseNode * list = node->pn_right;
931        switch (p->pn_type) {        if (list->pn_type == TOK_LEXICALSCOPE) {
932        case TOK_CASE:          list = list->pn_expr;
933          Stream_write_string(f, "case ");        }
934          instrument_expression(p->pn_left, f);        for (struct JSParseNode * p = list->pn_head; p != NULL; p = p->pn_next) {
935          Stream_write_string(f, ":\n");          Stream_printf(f, "%*s", indent, "");
936          break;          switch (p->pn_type) {
937        case TOK_DEFAULT:          case TOK_CASE:
938          Stream_write_string(f, "default:\n");            Stream_write_string(f, "case ");
939          break;            output_expression(p->pn_left, f, false);
940        default:            Stream_write_string(f, ":\n");
941          abort();            break;
942          break;          case TOK_DEFAULT:
943              Stream_write_string(f, "default:\n");
944              break;
945            default:
946              abort();
947              break;
948            }
949            instrument_statement(p->pn_right, f, indent + 2, false);
950        }        }
       instrument_statement(p->pn_right, f, indent + 2, false);  
951      }      }
952      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
953      Stream_write_string(f, "}\n");      Stream_write_string(f, "}\n");
# Line 731  Line 960 
960      assert(node->pn_arity == PN_BINARY);      assert(node->pn_arity == PN_BINARY);
961      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
962      Stream_write_string(f, "while (");      Stream_write_string(f, "while (");
963      instrument_expression(node->pn_left, f);      output_expression(node->pn_left, f, false);
964      Stream_write_string(f, ") {\n");      Stream_write_string(f, ") {\n");
965      instrument_statement(node->pn_right, f, indent + 2, false);      instrument_statement(node->pn_right, f, indent + 2, false);
966      Stream_write_string(f, "}\n");      Stream_write_string(f, "}\n");
# Line 744  Line 973 
973      Stream_write_string(f, "}\n");      Stream_write_string(f, "}\n");
974      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
975      Stream_write_string(f, "while (");      Stream_write_string(f, "while (");
976      instrument_expression(node->pn_right, f);      output_expression(node->pn_right, f, false);
977      Stream_write_string(f, ");\n");      Stream_write_string(f, ");\n");
978      break;      break;
979    case TOK_FOR:    case TOK_FOR:
980      assert(node->pn_arity == PN_BINARY);      assert(node->pn_arity == PN_BINARY);
981      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
     Stream_write_string(f, "for (");  
982      switch (node->pn_left->pn_type) {      switch (node->pn_left->pn_type) {
983      case TOK_IN:      case TOK_IN:
984        /* for/in */        /* for/in */
985        assert(node->pn_left->pn_arity == PN_BINARY);        assert(node->pn_left->pn_arity == PN_BINARY);
986        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);  
987        break;        break;
988      case TOK_RESERVED:      case TOK_RESERVED:
989        /* for (;;) */        /* for (;;) */
990        assert(node->pn_left->pn_arity == PN_TERNARY);        assert(node->pn_left->pn_arity == PN_TERNARY);
991          Stream_write_string(f, "for (");
992        if (node->pn_left->pn_kid1) {        if (node->pn_left->pn_kid1) {
993          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);  
         }  
994        }        }
995        Stream_write_string(f, ";");        Stream_write_string(f, ";");
996        if (node->pn_left->pn_kid2) {        if (node->pn_left->pn_kid2) {
997          Stream_write_char(f, ' ');          Stream_write_char(f, ' ');
998          instrument_expression(node->pn_left->pn_kid2, f);          output_expression(node->pn_left->pn_kid2, f, false);
999        }        }
1000        Stream_write_string(f, ";");        Stream_write_string(f, ";");
1001        if (node->pn_left->pn_kid3) {        if (node->pn_left->pn_kid3) {
1002          Stream_write_char(f, ' ');          Stream_write_char(f, ' ');
1003          instrument_expression(node->pn_left->pn_kid3, f);          output_expression(node->pn_left->pn_kid3, f, false);
1004        }        }
1005          Stream_write_char(f, ')');
1006        break;        break;
1007      default:      default:
1008        abort();        abort();
1009        break;        break;
1010      }      }
1011      Stream_write_string(f, ") {\n");      Stream_write_string(f, " {\n");
1012      instrument_statement(node->pn_right, f, indent + 2, false);      instrument_statement(node->pn_right, f, indent + 2, false);
1013      Stream_write_string(f, "}\n");      Stream_write_string(f, "}\n");
1014      break;      break;
# Line 810  Line 1016 
1016      assert(node->pn_arity == PN_UNARY);      assert(node->pn_arity == PN_UNARY);
1017      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
1018      Stream_write_string(f, "throw ");      Stream_write_string(f, "throw ");
1019      instrument_expression(node->pn_u.unary.kid, f);      output_expression(node->pn_u.unary.kid, f, false);
1020      Stream_write_string(f, ";\n");      Stream_write_string(f, ";\n");
1021      break;      break;
1022    case TOK_TRY:    case TOK_TRY:
# Line 819  Line 1025 
1025      instrument_statement(node->pn_kid1, f, indent + 2, false);      instrument_statement(node->pn_kid1, f, indent + 2, false);
1026      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
1027      Stream_write_string(f, "}\n");      Stream_write_string(f, "}\n");
1028      {      if (node->pn_kid2) {
1029        for (JSParseNode * catch = node->pn_kid2; catch != NULL; catch = catch->pn_kid2) {        assert(node->pn_kid2->pn_type == TOK_RESERVED);
1030          for (JSParseNode * scope = node->pn_kid2->pn_head; scope != NULL; scope = scope->pn_next) {
1031            assert(scope->pn_type == TOK_LEXICALSCOPE);
1032            JSParseNode * catch = scope->pn_expr;
1033          assert(catch->pn_type == TOK_CATCH);          assert(catch->pn_type == TOK_CATCH);
1034          Stream_printf(f, "%*s", indent, "");          Stream_printf(f, "%*s", indent, "");
1035          Stream_write_string(f, "catch (");          Stream_write_string(f, "catch (");
1036          assert(catch->pn_kid1->pn_arity == PN_NAME);          output_expression(catch->pn_kid1, f, false);
1037          print_string_atom(catch->pn_kid1->pn_atom, f);          if (catch->pn_kid2) {
         if (catch->pn_kid1->pn_expr) {  
1038            Stream_write_string(f, " if ");            Stream_write_string(f, " if ");
1039            instrument_expression(catch->pn_kid1->pn_expr, f);            output_expression(catch->pn_kid2, f, false);
1040          }          }
1041          Stream_write_string(f, ") {\n");          Stream_write_string(f, ") {\n");
1042          instrument_statement(catch->pn_kid3, f, indent + 2, false);          instrument_statement(catch->pn_kid3, f, indent + 2, false);
# Line 863  Line 1071 
1071      assert(node->pn_arity == PN_BINARY);      assert(node->pn_arity == PN_BINARY);
1072      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
1073      Stream_write_string(f, "with (");      Stream_write_string(f, "with (");
1074      instrument_expression(node->pn_left, f);      output_expression(node->pn_left, f, false);
1075      Stream_write_string(f, ") {\n");      Stream_write_string(f, ") {\n");
1076      instrument_statement(node->pn_right, f, indent + 2, false);      instrument_statement(node->pn_right, f, indent + 2, false);
1077      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
1078      Stream_write_string(f, "}\n");      Stream_write_string(f, "}\n");
1079      break;      break;
1080    case TOK_VAR:    case TOK_VAR:
1081      instrument_var_statement(node, f, indent);      Stream_printf(f, "%*s", indent, "");
1082        output_expression(node, f, false);
1083      Stream_write_string(f, ";\n");      Stream_write_string(f, ";\n");
1084      break;      break;
1085    case TOK_RETURN:    case TOK_RETURN:
# Line 879  Line 1088 
1088      Stream_write_string(f, "return");      Stream_write_string(f, "return");
1089      if (node->pn_kid != NULL) {      if (node->pn_kid != NULL) {
1090        Stream_write_char(f, ' ');        Stream_write_char(f, ' ');
1091        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, true);
1092      }      }
1093      Stream_write_string(f, ";\n");      Stream_write_string(f, ";\n");
1094      break;      break;
# Line 887  Line 1096 
1096      assert(node->pn_arity == PN_UNARY);      assert(node->pn_arity == PN_UNARY);
1097      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
1098      if (node->pn_kid != NULL) {      if (node->pn_kid != NULL) {
1099        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, true);
1100      }      }
1101      Stream_write_string(f, ";\n");      Stream_write_string(f, ";\n");
1102      break;      break;
1103    case TOK_COLON:    case TOK_COLON:
1104      {
1105      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 ...  
     */  
1106      Stream_printf(f, "%*s", indent < 2? 0: indent - 2, "");      Stream_printf(f, "%*s", indent < 2? 0: indent - 2, "");
1107      print_string_atom(node->pn_atom, f);      print_string_atom(node->pn_atom, f);
1108      Stream_write_string(f, ":\n");      Stream_write_string(f, ":\n");
1109      /*      JSParseNode * labelled = node->pn_expr;
1110      ... use output_statement instead of instrument_statement.      if (labelled->pn_type == TOK_LEXICALSCOPE) {
1111      */        labelled = labelled->pn_expr;
1112      output_statement(node->pn_expr, f, indent, false);      }
1113        if (labelled->pn_type == TOK_LC) {
1114          /* labelled block */
1115          Stream_printf(f, "%*s", indent, "");
1116          Stream_write_string(f, "{\n");
1117          instrument_statement(labelled, f, indent + 2, false);
1118          Stream_printf(f, "%*s", indent, "");
1119          Stream_write_string(f, "}\n");
1120        }
1121        else {
1122          /*
1123          This one is tricky: can't output instrumentation between the label and the
1124          statement it's supposed to label, so use output_statement instead of
1125          instrument_statement.
1126          */
1127          output_statement(labelled, f, indent, false);
1128        }
1129        break;
1130      }
1131      case TOK_LEXICALSCOPE:
1132        /* let statement */
1133        assert(node->pn_arity == PN_NAME);
1134        switch (node->pn_expr->pn_type) {
1135        case TOK_LET:
1136          /* let statement */
1137          assert(node->pn_expr->pn_arity == PN_BINARY);
1138          instrument_statement(node->pn_expr, f, indent, false);
1139          break;
1140        case TOK_LC:
1141          /* block */
1142          Stream_printf(f, "%*s", indent, "");
1143          Stream_write_string(f, "{\n");
1144          instrument_statement(node->pn_expr, f, indent + 2, false);
1145          Stream_printf(f, "%*s", indent, "");
1146          Stream_write_string(f, "}\n");
1147          break;
1148        case TOK_FOR:
1149          instrument_statement(node->pn_expr, f, indent, false);
1150          break;
1151        default:
1152          abort();
1153          break;
1154        }
1155        break;
1156      case TOK_LET:
1157        switch (node->pn_arity) {
1158        case PN_BINARY:
1159          /* let statement */
1160          Stream_printf(f, "%*s", indent, "");
1161          Stream_write_string(f, "let (");
1162          assert(node->pn_left->pn_type == TOK_LP);
1163          assert(node->pn_left->pn_arity == PN_LIST);
1164          instrument_declarations(node->pn_left, f);
1165          Stream_write_string(f, ") {\n");
1166          instrument_statement(node->pn_right, f, indent + 2, false);
1167          Stream_printf(f, "%*s", indent, "");
1168          Stream_write_string(f, "}\n");
1169          break;
1170        case PN_LIST:
1171          /* let definition */
1172          Stream_printf(f, "%*s", indent, "");
1173          output_expression(node, f, false);
1174          Stream_write_string(f, ";\n");
1175          break;
1176        default:
1177          abort();
1178          break;
1179        }
1180        break;
1181      case TOK_DEBUGGER:
1182        Stream_printf(f, "%*s", indent, "");
1183        Stream_write_string(f, "debugger;\n");
1184      break;      break;
1185    default:    default:
1186      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);
1187    }    }
1188  }  }
1189    
# Line 916  Line 1193 
1193  TOK_EXPORT, TOK_IMPORT are not handled.  TOK_EXPORT, TOK_IMPORT are not handled.
1194  */  */
1195  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) {
1196    if (node->pn_type != TOK_LC) {    if (node->pn_type != TOK_LC && node->pn_type != TOK_LEXICALSCOPE) {
1197      uint16_t line = node->pn_pos.begin.lineno;      uint16_t line = node->pn_pos.begin.lineno;
1198      if (line > num_lines) {      if (line > num_lines) {
1199        fatal("%s: script contains more than 65,535 lines", file_id);        fatal("file %s contains more than 65,535 lines", file_id);
1200      }      }
1201    
1202      /* the root node has line number 0 */      /* the root node has line number 0 */
# Line 966  Line 1243 
1243    return true;    return true;
1244  }  }
1245    
1246    static void error_reporter(JSContext * context, const char * message, JSErrorReport * report) {
1247      warn_source(file_id, report->lineno, "%s", message);
1248    }
1249    
1250  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) {
1251    file_id = id;    file_id = id;
1252    
   /* 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);  
   }  
   
1253    /* parse the javascript */    /* parse the javascript */
1254    JSParseNode * node = js_ParseTokenStream(context, global, token_stream);    JSParseContext parse_context;
1255      if (! js_InitParseContext(context, &parse_context, NULL, NULL, characters, num_characters, NULL, NULL, 1)) {
1256        fatal("cannot create token stream from file %s", file_id);
1257      }
1258      JSErrorReporter old_error_reporter = JS_SetErrorReporter(context, error_reporter);
1259      JSParseNode * node = js_ParseScript(context, global, &parse_context);
1260    if (node == NULL) {    if (node == NULL) {
1261      fatal("parse error in file: %s", file_id);      js_ReportUncaughtException(context);
1262        fatal("parse error in file %s", file_id);
1263    }    }
1264      JS_SetErrorReporter(context, old_error_reporter);
1265    num_lines = node->pn_pos.end.lineno;    num_lines = node->pn_pos.end.lineno;
1266    lines = xmalloc(num_lines);    lines = xmalloc(num_lines);
1267    for (unsigned int i = 0; i < num_lines; i++) {    for (unsigned int i = 0; i < num_lines; i++) {
# Line 998  Line 1280 
1280    size_t i = 0;    size_t i = 0;
1281    while (i < num_characters) {    while (i < num_characters) {
1282      if (line_number == UINT16_MAX) {      if (line_number == UINT16_MAX) {
1283        fatal("%s: script has more than 65,535 lines", file_id);        fatal("file %s contains more than 65,535 lines", file_id);
1284      }      }
1285      line_number++;      line_number++;
1286      size_t line_start = i;      size_t line_start = i;
# Line 1061  Line 1343 
1343    
1344    Stream * instrumented = Stream_new(0);    Stream * instrumented = Stream_new(0);
1345    instrument_statement(node, instrumented, 0, false);    instrument_statement(node, instrumented, 0, false);
1346      js_FinishParseContext(context, &parse_context);
1347    
1348    /* write line number info to the output */    /* write line number info to the output */
1349    Stream_write_string(output, "/* automatically generated by JSCoverage - do not edit */\n");    Stream_write_string(output, "/* automatically generated by JSCoverage - do not edit */\n");
1350    Stream_write_string(output, "if (! top._$jscoverage) {\n  top._$jscoverage = {};\n}\n");    if (jscoverage_mozilla) {
1351    Stream_write_string(output, "var _$jscoverage = top._$jscoverage;\n");      Stream_write_string(output, "try {\n");
1352        Stream_write_string(output, "  Components.utils.import('resource://gre/modules/jscoverage.jsm');\n");
1353        Stream_printf(output, "  dump('%s: successfully imported jscoverage module\\n');\n", id);
1354        Stream_write_string(output, "}\n");
1355        Stream_write_string(output, "catch (e) {\n");
1356        Stream_write_string(output, "  _$jscoverage = {};\n");
1357        Stream_printf(output, "  dump('%s: failed to import jscoverage module - coverage not available for this file\\n');\n", id);
1358        Stream_write_string(output, "}\n");
1359      }
1360      else {
1361        Stream_write_string(output, "if (! top._$jscoverage) {\n  top._$jscoverage = {};\n}\n");
1362        Stream_write_string(output, "var _$jscoverage = top._$jscoverage;\n");
1363      }
1364    Stream_printf(output, "if (! _$jscoverage['%s']) {\n", file_id);    Stream_printf(output, "if (! _$jscoverage['%s']) {\n", file_id);
1365    Stream_printf(output, "  _$jscoverage['%s'] = [];\n", file_id);    Stream_printf(output, "  _$jscoverage['%s'] = [];\n", file_id);
1366    for (int i = 0; i < num_lines; i++) {    for (int i = 0; i < num_lines; i++) {
# Line 1090  Line 1385 
1385    /* conditionals */    /* conditionals */
1386    for (struct IfDirective * if_directive = if_directives; if_directive != NULL; if_directive = if_directive->next) {    for (struct IfDirective * if_directive = if_directives; if_directive != NULL; if_directive = if_directive->next) {
1387      Stream_write_string(output, "if (!(");      Stream_write_string(output, "if (!(");
1388      for (const jschar * p = if_directive->condition_start; p < if_directive->condition_end; p++) {      print_javascript(if_directive->condition_start, if_directive->condition_end - if_directive->condition_start, output);
       jschar c = *p;  
       if (c == '\t' || (32 <= c && c <= 126)) {  
         Stream_write_char(output, c);  
       }  
       else {  
         Stream_printf(output, "\\u%04x", c);  
       }  
     }  
1389      Stream_write_string(output, ")) {\n");      Stream_write_string(output, ")) {\n");
1390      Stream_printf(output, "  _$jscoverage['%s'].conditionals[%d] = %d;\n", file_id, if_directive->start_line, if_directive->end_line);      Stream_printf(output, "  _$jscoverage['%s'].conditionals[%d] = %d;\n", file_id, if_directive->start_line, if_directive->end_line);
1391      Stream_write_string(output, "}\n");      Stream_write_string(output, "}\n");
# Line 1149  Line 1436 
1436            /* line feed (new line) */            /* line feed (new line) */
1437            done = true;            done = true;
1438            break;            break;
1439            /* IE doesn't support this */
1440            /*
1441          case 0xb:          case 0xb:
           /* vertical tab */  
1442            Stream_write_string(output, "\\v");            Stream_write_string(output, "\\v");
1443            break;            break;
1444            */
1445          case 0xc:          case 0xc:
1446            /* form feed */            /* form feed */
1447            Stream_write_string(output, "\\f");            Stream_write_string(output, "\\f");
# Line 1207  Line 1496 
1496            /* line feed (new line) */            /* line feed (new line) */
1497            done = true;            done = true;
1498            break;            break;
1499            /* IE doesn't support this */
1500            /*
1501          case 0xb:          case 0xb:
           /* vertical tab */  
1502            Stream_write_string(output, "\\v");            Stream_write_string(output, "\\v");
1503            break;            break;
1504            */
1505          case 0xc:          case 0xc:
1506            /* form feed */            /* form feed */
1507            Stream_write_string(output, "\\f");            Stream_write_string(output, "\\f");
# Line 1337  Line 1628 
1628  }  }
1629    
1630  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) {
1631      int result = 0;
1632    
1633    jschar * base = js_InflateString(context, (char *) json, &length);    jschar * base = js_InflateString(context, (char *) json, &length);
1634    if (base == NULL) {    if (base == NULL) {
1635      fatal("out of memory");      fatal("out of memory");
# Line 1349  Line 1642 
1642    
1643    JS_free(context, base);    JS_free(context, base);
1644    
1645    JSTokenStream * token_stream = js_NewTokenStream(context, parenthesized_json, length + 2, NULL, 1, NULL);    JSParseContext parse_context;
1646    if (token_stream == NULL) {    if (! js_InitParseContext(context, &parse_context, NULL, NULL, parenthesized_json, length + 2, NULL, NULL, 1)) {
1647      fatal("cannot create token stream");      free(parenthesized_json);
1648        return -1;
1649    }    }
1650      JSParseNode * root = js_ParseScript(context, global, &parse_context);
   JSParseNode * root = js_ParseTokenStream(context, global, token_stream);  
1651    free(parenthesized_json);    free(parenthesized_json);
1652    if (root == NULL) {    if (root == NULL) {
1653      return -1;      result = -1;
1654        goto done;
1655    }    }
1656    
1657    /* root node must be TOK_LC */    /* root node must be TOK_LC */
1658    if (root->pn_type != TOK_LC) {    if (root->pn_type != TOK_LC) {
1659      return -1;      result = -1;
1660        goto done;
1661    }    }
1662    JSParseNode * semi = root->pn_u.list.head;    JSParseNode * semi = root->pn_u.list.head;
1663    
1664    /* 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 */
1665    if (semi->pn_type != TOK_SEMI || semi->pn_next != NULL) {    if (semi->pn_type != TOK_SEMI || semi->pn_next != NULL) {
1666      return -1;      result = -1;
1667        goto done;
1668    }    }
1669    JSParseNode * parenthesized = semi->pn_kid;    JSParseNode * parenthesized = semi->pn_kid;
1670    
1671    /* this must be a parenthesized expression */    /* this must be a parenthesized expression */
1672    if (parenthesized->pn_type != TOK_RP) {    if (parenthesized->pn_type != TOK_RP) {
1673      return -1;      result = -1;
1674        goto done;
1675    }    }
1676    JSParseNode * object = parenthesized->pn_kid;    JSParseNode * object = parenthesized->pn_kid;
1677    
1678    /* this must be an object literal */    /* this must be an object literal */
1679    if (object->pn_type != TOK_RC) {    if (object->pn_type != TOK_RC) {
1680      return -1;      result = -1;
1681        goto done;
1682    }    }
1683    
1684    for (JSParseNode * p = object->pn_head; p != NULL; p = p->pn_next) {    for (JSParseNode * p = object->pn_head; p != NULL; p = p->pn_next) {
1685      /* every element of this list must be TOK_COLON */      /* every element of this list must be TOK_COLON */
1686      if (p->pn_type != TOK_COLON) {      if (p->pn_type != TOK_COLON) {
1687        return -1;        result = -1;
1688          goto done;
1689      }      }
1690    
1691      /* the key must be a string representing the file */      /* the key must be a string representing the file */
1692      JSParseNode * key = p->pn_left;      JSParseNode * key = p->pn_left;
1693      if (key->pn_type != TOK_STRING || ! ATOM_IS_STRING(key->pn_atom)) {      if (key->pn_type != TOK_STRING || ! ATOM_IS_STRING(key->pn_atom)) {
1694        return -1;        result = -1;
1695          goto done;
1696      }      }
1697      char * id_bytes = JS_GetStringBytes(ATOM_TO_STRING(key->pn_atom));      char * id_bytes = JS_GetStringBytes(ATOM_TO_STRING(key->pn_atom));
1698    
1699      /* the value must be an object literal OR an array */      /* the value must be an object literal OR an array */
1700      JSParseNode * value = p->pn_right;      JSParseNode * value = p->pn_right;
1701      if (! (value->pn_type == TOK_RC || value->pn_type == TOK_RB)) {      if (! (value->pn_type == TOK_RC || value->pn_type == TOK_RB)) {
1702        return -1;        result = -1;
1703          goto done;
1704      }      }
1705    
1706      JSParseNode * array = NULL;      JSParseNode * array = NULL;
# Line 1411  Line 1712 
1712      else if (value->pn_type == TOK_RC) {      else if (value->pn_type == TOK_RC) {
1713        /* an object literal */        /* an object literal */
1714        if (value->pn_count != 2) {        if (value->pn_count != 2) {
1715          return -1;          result = -1;
1716            goto done;
1717        }        }
1718        for (JSParseNode * element = value->pn_head; element != NULL; element = element->pn_next) {        for (JSParseNode * element = value->pn_head; element != NULL; element = element->pn_next) {
1719          if (element->pn_type != TOK_COLON) {          if (element->pn_type != TOK_COLON) {
1720            return -1;            result = -1;
1721              goto done;
1722          }          }
1723          JSParseNode * left = element->pn_left;          JSParseNode * left = element->pn_left;
1724          if (left->pn_type != TOK_STRING || ! ATOM_IS_STRING(left->pn_atom)) {          if (left->pn_type != TOK_STRING || ! ATOM_IS_STRING(left->pn_atom)) {
1725            return -1;            result = -1;
1726              goto done;
1727          }          }
1728          const char * s = JS_GetStringBytes(ATOM_TO_STRING(left->pn_atom));          const char * s = JS_GetStringBytes(ATOM_TO_STRING(left->pn_atom));
1729          if (strcmp(s, "coverage") == 0) {          if (strcmp(s, "coverage") == 0) {
1730            array = element->pn_right;            array = element->pn_right;
1731            if (array->pn_type != TOK_RB) {            if (array->pn_type != TOK_RB) {
1732              return -1;              result = -1;
1733                goto done;
1734            }            }
1735          }          }
1736          else if (strcmp(s, "source") == 0) {          else if (strcmp(s, "source") == 0) {
1737            source = element->pn_right;            source = element->pn_right;
1738            if (source->pn_type != TOK_RB) {            if (source->pn_type != TOK_RB) {
1739              return -1;              result = -1;
1740                goto done;
1741            }            }
1742          }          }
1743          else {          else {
1744            return -1;            result = -1;
1745              goto done;
1746          }          }
1747        }        }
1748      }      }
1749      else {      else {
1750        return -1;        result = -1;
1751          goto done;
1752      }      }
1753    
1754      if (array == NULL) {      if (array == NULL) {
1755        return -1;        result = -1;
1756          goto done;
1757      }      }
1758    
1759      /* look up the file in the coverage table */      /* look up the file in the coverage table */
# Line 1456  Line 1765 
1765        file_coverage->id = id;        file_coverage->id = id;
1766        file_coverage->num_coverage_lines = array->pn_count;        file_coverage->num_coverage_lines = array->pn_count;
1767        file_coverage->coverage_lines = xnew(int, array->pn_count);        file_coverage->coverage_lines = xnew(int, array->pn_count);
1768        if (source == NULL) {        file_coverage->source_lines = NULL;
         file_coverage->source_lines = NULL;  
       }  
       else {  
         file_coverage->num_source_lines = source->pn_count;  
         file_coverage->source_lines = xnew(char *, source->pn_count);  
         uint32 i = 0;  
         for (JSParseNode * element = source->pn_head; element != NULL; element = element->pn_next, i++) {  
           if (element->pn_type != TOK_STRING) {  
             return -1;  
           }  
           file_coverage->source_lines[i] = xstrdup(JS_GetStringBytes(ATOM_TO_STRING(element->pn_atom)));  
         }  
         assert(i == source->pn_count);  
       }  
1769    
1770        /* set coverage for all lines */        /* set coverage for all lines */
1771        uint32 i = 0;        uint32 i = 0;
# Line 1482  Line 1777 
1777            file_coverage->coverage_lines[i] = -1;            file_coverage->coverage_lines[i] = -1;
1778          }          }
1779          else {          else {
1780            return -1;            result = -1;
1781              goto done;
1782          }          }
1783        }        }
1784        assert(i == array->pn_count);        assert(i == array->pn_count);
# Line 1498  Line 1794 
1794        /* sanity check */        /* sanity check */
1795        assert(strcmp(file_coverage->id, id_bytes) == 0);        assert(strcmp(file_coverage->id, id_bytes) == 0);
1796        if (file_coverage->num_coverage_lines != array->pn_count) {        if (file_coverage->num_coverage_lines != array->pn_count) {
1797          return -2;          result = -2;
1798            goto done;
1799        }        }
1800    
1801        /* merge the coverage */        /* merge the coverage */
# Line 1506  Line 1803 
1803        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++) {
1804          if (element->pn_type == TOK_NUMBER) {          if (element->pn_type == TOK_NUMBER) {
1805            if (file_coverage->coverage_lines[i] == -1) {            if (file_coverage->coverage_lines[i] == -1) {
1806              return -2;              result = -2;
1807                goto done;
1808            }            }
1809            file_coverage->coverage_lines[i] += (int) element->pn_dval;            file_coverage->coverage_lines[i] += (int) element->pn_dval;
1810          }          }
1811          else if (element->pn_type == TOK_PRIMARY && element->pn_op == JSOP_NULL) {          else if (element->pn_type == TOK_PRIMARY && element->pn_op == JSOP_NULL) {
1812            if (file_coverage->coverage_lines[i] != -1) {            if (file_coverage->coverage_lines[i] != -1) {
1813              return -2;              result = -2;
1814                goto done;
1815            }            }
1816          }          }
1817          else {          else {
1818            return -1;            result = -1;
1819              goto done;
1820          }          }
1821        }        }
1822        assert(i == array->pn_count);        assert(i == array->pn_count);
1823        }
1824    
1825        /* if this JSON file has source, use it */      /* if this JSON file has source, use it */
1826        if (file_coverage->source_lines == NULL && source != NULL) {      if (file_coverage->source_lines == NULL && source != NULL) {
1827          file_coverage->num_source_lines = source->pn_count;        file_coverage->num_source_lines = source->pn_count;
1828          file_coverage->source_lines = xnew(char *, source->pn_count);        file_coverage->source_lines = xnew(char *, source->pn_count);
1829          uint32 i = 0;        uint32 i = 0;
1830          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++) {
1831            if (element->pn_type != TOK_STRING) {          if (element->pn_type != TOK_STRING) {
1832              return -1;            result = -1;
1833            }            goto done;
           file_coverage->source_lines[i] = xstrdup(JS_GetStringBytes(ATOM_TO_STRING(element->pn_atom)));  
1834          }          }
1835          assert(i == source->pn_count);          file_coverage->source_lines[i] = xstrdup(JS_GetStringBytes(ATOM_TO_STRING(element->pn_atom)));
1836        }        }
1837          assert(i == source->pn_count);
1838      }      }
1839    }    }
1840    
1841    return 0;  done:
1842      js_FinishParseContext(context, &parse_context);
1843      return result;
1844  }  }

Legend:
Removed from v.240  
changed lines
  Added in v.387

  ViewVC Help
Powered by ViewVC 1.1.24