/[jscoverage]/trunk/instrument-js.cpp
ViewVC logotype

Diff of /trunk/instrument-js.cpp

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

trunk/instrument-js.c revision 245 by siliconforks, Sun Oct 5 18:05:12 2008 UTC trunk/instrument-js.cpp revision 402 by siliconforks, Tue Dec 9 09:47:43 2008 UTC
# Line 1  Line 1 
1  /*  /*
2      instrument-js.c - JavaScript instrumentation routines      instrument-js.cpp - JavaScript instrumentation routines
3      Copyright (C) 2007, 2008 siliconforks.com      Copyright (C) 2007, 2008 siliconforks.com
4    
5      This program is free software; you can redistribute it and/or modify      This program is free software; you can redistribute it and/or modify
# 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>
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 48  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 62  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 73  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 88  Line 111 
111    JS_DestroyRuntime(runtime);    JS_DestroyRuntime(runtime);
112  }  }
113    
114    static void print_javascript(const jschar * characters, size_t num_characters, Stream * f) {
115      for (size_t i = 0; i < num_characters; i++) {
116        jschar c = characters[i];
117        /*
118        XXX does not handle no-break space, other unicode "space separator"
119        */
120        switch (c) {
121        case 0x9:
122        case 0xB:
123        case 0xC:
124          Stream_write_char(f, c);
125          break;
126        default:
127          if (32 <= c && c <= 126) {
128            Stream_write_char(f, c);
129          }
130          else {
131            Stream_printf(f, "\\u%04x", c);
132          }
133          break;
134        }
135      }
136    }
137    
138  static void print_string(JSString * s, Stream * f) {  static void print_string(JSString * s, Stream * f) {
139    size_t length = JSSTRING_LENGTH(s);    size_t length = JSSTRING_LENGTH(s);
140    jschar * characters = JSSTRING_CHARS(s);    jschar * characters = JSSTRING_CHARS(s);
# Line 122  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 171  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 181  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 214  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      switch (p->pn_arity) {
309      case PN_UNARY:
310        p = p->pn_kid;
311        if (p->pn_type == TOK_YIELD) {
312          /* for generator expressions */
313          p = p->pn_kid;
314        }
315        output_expression(p, f, false);
316        break;
317      case PN_LIST:
318        /*
319        When the array comprehension contains "if (0)", it will be optimized away and
320        the result will be an empty TOK_LC list.
321        */
322        assert(p->pn_type == TOK_LC);
323        assert(p->pn_head == NULL);
324        /* the "1" is arbitrary (since the list is empty) */
325        Stream_write_char(f, '1');
326        break;
327      default:
328        abort();
329        break;
330      }
331    
332      p = for_node;
333      while (p->pn_type == TOK_FOR) {
334        Stream_write_char(f, ' ');
335        output_for_in(p, f);
336        p = p->pn_right;
337      }
338      if (p->pn_type == TOK_LC) {
339        /* this is the optimized-away "if (0)" */
340        Stream_write_string(f, " if (0)");
341      }
342      else if (if_node) {
343        Stream_write_string(f, " if (");
344        output_expression(if_node->pn_kid1, f, false);
345        Stream_write_char(f, ')');
346      }
347    }
348    
349  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) {
350      assert(node->pn_type == TOK_FUNCTION);
351    assert(node->pn_arity == PN_FUNC);    assert(node->pn_arity == PN_FUNC);
352    assert(ATOM_IS_OBJECT(node->pn_funAtom));    JSObject * object = node->pn_funpob->object;
   JSObject * object = ATOM_TO_OBJECT(node->pn_funAtom);  
353    assert(JS_ObjectIsFunction(context, object));    assert(JS_ObjectIsFunction(context, object));
354    JSFunction * function = (JSFunction *) JS_GetPrivate(context, object);    JSFunction * function = (JSFunction *) JS_GetPrivate(context, object);
355    assert(function);    assert(function);
356    assert(object == function->object);    assert(object == &function->object);
357    Stream_printf(f, "%*s", indent, "");    Stream_printf(f, "%*s", indent, "");
358    if (type == FUNCTION_NORMAL) {    if (type == FUNCTION_NORMAL) {
359      Stream_write_string(f, "function");      Stream_write_string(f, "function ");
360    }    }
361    
362    /* function name */    /* function name */
363    if (function->atom) {    if (function->atom) {
     Stream_write_char(f, ' ');  
364      print_string_atom(function->atom, f);      print_string_atom(function->atom, f);
365    }    }
366    
367    /* function parameters */    /*
368    Stream_write_string(f, "(");    function parameters - see JS_DecompileFunction in jsapi.cpp, which calls
369    JSAtom ** params = xnew(JSAtom *, function->nargs);    js_DecompileFunction in jsopcode.cpp
370    for (int i = 0; i < function->nargs; i++) {    */
371      /* initialize to NULL for sanity check */    Stream_write_char(f, '(');
372      params[i] = NULL;    JSArenaPool pool;
373    }    JS_INIT_ARENA_POOL(&pool, "instrument_function", 256, 1, &context->scriptStackQuota);
374    JSScope * scope = OBJ_SCOPE(object);    jsuword * local_names = NULL;
375    for (JSScopeProperty * scope_property = SCOPE_LAST_PROP(scope); scope_property != NULL; scope_property = scope_property->parent) {    if (JS_GET_LOCAL_NAME_COUNT(function)) {
376      if (scope_property->getter != js_GetArgument) {      local_names = js_GetLocalNameArray(context, function, &pool);
377        continue;      if (local_names == NULL) {
378          fatal("out of memory");
379      }      }
     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);  
380    }    }
381      bool destructuring = false;
382    for (int i = 0; i < function->nargs; i++) {    for (int i = 0; i < function->nargs; i++) {
     assert(params[i] != NULL);  
383      if (i > 0) {      if (i > 0) {
384        Stream_write_string(f, ", ");        Stream_write_string(f, ", ");
385      }      }
386      if (ATOM_IS_STRING(params[i])) {      JSAtom * param = JS_LOCAL_NAME_TO_ATOM(local_names[i]);
387        print_string_atom(params[i], f);      if (param == NULL) {
388          destructuring = true;
389          JSParseNode * expression = NULL;
390          assert(node->pn_body->pn_type == TOK_LC || node->pn_body->pn_type == TOK_SEQ);
391          JSParseNode * semi = node->pn_body->pn_head;
392          assert(semi->pn_type == TOK_SEMI);
393          JSParseNode * comma = semi->pn_kid;
394          assert(comma->pn_type == TOK_COMMA);
395          for (JSParseNode * p = comma->pn_head; p != NULL; p = p->pn_next) {
396            assert(p->pn_type == TOK_ASSIGN);
397            JSParseNode * rhs = p->pn_right;
398            assert(JSSTRING_LENGTH(ATOM_TO_STRING(rhs->pn_atom)) == 0);
399            if (rhs->pn_slot == i) {
400              expression = p->pn_left;
401              break;
402            }
403          }
404          assert(expression != NULL);
405          output_expression(expression, f, false);
406        }
407        else {
408          print_string_atom(param, f);
409      }      }
410    }    }
411      JS_FinishArenaPool(&pool);
412    Stream_write_string(f, ") {\n");    Stream_write_string(f, ") {\n");
   free(params);  
413    
414    /* function body */    /* function body */
415    instrument_statement(node->pn_body, f, indent + 2, false);    if (function->flags & JSFUN_EXPR_CLOSURE) {
416        /* expression closure - use output_statement instead of instrument_statement */
417        if (node->pn_body->pn_type == TOK_SEQ) {
418          assert(node->pn_body->pn_arity == PN_LIST);
419          assert(node->pn_body->pn_count == 2);
420          output_statement(node->pn_body->pn_head->pn_next, f, indent + 2, false);
421        }
422        else {
423          output_statement(node->pn_body, f, indent + 2, false);
424        }
425      }
426      else {
427        assert(node->pn_body->pn_type == TOK_LC);
428        assert(node->pn_body->pn_arity == PN_LIST);
429        JSParseNode * p = node->pn_body->pn_head;
430        if (destructuring) {
431          p = p->pn_next;
432        }
433        for (; p != NULL; p = p->pn_next) {
434          instrument_statement(p, f, indent + 2, false);
435        }
436      }
437    
438    Stream_write_string(f, "}\n");    Stream_write_char(f, '}');
439  }  }
440    
441  static void instrument_function_call(JSParseNode * node, Stream * f) {  static void instrument_function_call(JSParseNode * node, Stream * f) {
442    instrument_expression(node->pn_head, f);    JSParseNode * function_node = node->pn_head;
443      if (function_node->pn_type == TOK_FUNCTION) {
444        JSObject * object = function_node->pn_funpob->object;
445        assert(JS_ObjectIsFunction(context, object));
446        JSFunction * function = (JSFunction *) JS_GetPrivate(context, object);
447        assert(function);
448        assert(object == &function->object);
449    
450        if (function_node->pn_flags & TCF_GENEXP_LAMBDA) {
451          /* it's a generator expression */
452          Stream_write_char(f, '(');
453          output_array_comprehension_or_generator_expression(function_node->pn_body, f);
454          Stream_write_char(f, ')');
455          return;
456        }
457      }
458      output_expression(function_node, f, false);
459    Stream_write_char(f, '(');    Stream_write_char(f, '(');
460    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) {
461      if (p != node->pn_head->pn_next) {      if (p != node->pn_head->pn_next) {
462        Stream_write_string(f, ", ");        Stream_write_string(f, ", ");
463      }      }
464      instrument_expression(p, f);      output_expression(p, f, false);
465    }    }
466    Stream_write_char(f, ')');    Stream_write_char(f, ')');
467  }  }
468    
469    static void instrument_declarations(JSParseNode * list, Stream * f) {
470      assert(list->pn_arity == PN_LIST);
471      for (JSParseNode * p = list->pn_head; p != NULL; p = p->pn_next) {
472        if (p != list->pn_head) {
473          Stream_write_string(f, ", ");
474        }
475        output_expression(p, f, false);
476      }
477    }
478    
479  /*  /*
480  See <Expressions> in jsparse.h.  See <Expressions> in jsparse.h.
481  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 489 
489  TOK_INSTANCEOF  binary  TOK_INSTANCEOF  binary
490  TOK_IN          binary  TOK_IN          binary
491  */  */
492  static void instrument_expression(JSParseNode * node, Stream * f) {  static void output_expression(JSParseNode * node, Stream * f, bool parenthesize_object_literals) {
493    switch (node->pn_type) {    switch (node->pn_type) {
494    case TOK_FUNCTION:    case TOK_FUNCTION:
495        Stream_write_char(f, '(');
496      instrument_function(node, f, 0, FUNCTION_NORMAL);      instrument_function(node, f, 0, FUNCTION_NORMAL);
497        Stream_write_char(f, ')');
498      break;      break;
499    case TOK_COMMA:    case TOK_COMMA:
500      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) {
501        if (p != node->pn_head) {        if (p != node->pn_head) {
502          Stream_write_string(f, ", ");          Stream_write_string(f, ", ");
503        }        }
504        instrument_expression(p, f);        output_expression(p, f, parenthesize_object_literals);
505      }      }
506      break;      break;
507    case TOK_ASSIGN:    case TOK_ASSIGN:
508      instrument_expression(node->pn_left, f);      output_expression(node->pn_left, f, parenthesize_object_literals);
509      Stream_write_char(f, ' ');      Stream_write_char(f, ' ');
510      switch (node->pn_op) {      switch (node->pn_op) {
511      case JSOP_ADD:      case JSOP_ADD:
# Line 336  Line 526 
526        break;        break;
527      }      }
528      Stream_write_string(f, "= ");      Stream_write_string(f, "= ");
529      instrument_expression(node->pn_right, f);      output_expression(node->pn_right, f, false);
530      break;      break;
531    case TOK_HOOK:    case TOK_HOOK:
532      instrument_expression(node->pn_kid1, f);      output_expression(node->pn_kid1, f, parenthesize_object_literals);
533      Stream_write_string(f, "? ");      Stream_write_string(f, "? ");
534      instrument_expression(node->pn_kid2, f);      output_expression(node->pn_kid2, f, false);
535      Stream_write_string(f, ": ");      Stream_write_string(f, ": ");
536      instrument_expression(node->pn_kid3, f);      output_expression(node->pn_kid3, f, false);
537      break;      break;
538    case TOK_OR:    case TOK_OR:
     instrument_expression(node->pn_left, f);  
     Stream_write_string(f, " || ");  
     instrument_expression(node->pn_right, f);  
     break;  
539    case TOK_AND:    case TOK_AND:
     instrument_expression(node->pn_left, f);  
     Stream_write_string(f, " && ");  
     instrument_expression(node->pn_right, f);  
     break;  
540    case TOK_BITOR:    case TOK_BITOR:
541    case TOK_BITXOR:    case TOK_BITXOR:
542    case TOK_BITAND:    case TOK_BITAND:
# Line 367  Line 549 
549    case TOK_DIVOP:    case TOK_DIVOP:
550      switch (node->pn_arity) {      switch (node->pn_arity) {
551      case PN_BINARY:      case PN_BINARY:
552        instrument_expression(node->pn_left, f);        output_expression(node->pn_left, f, parenthesize_object_literals);
553        Stream_printf(f, " %s ", get_op(node->pn_op));        Stream_printf(f, " %s ", get_op(node->pn_op));
554        instrument_expression(node->pn_right, f);        output_expression(node->pn_right, f, false);
555        break;        break;
556      case PN_LIST:      case PN_LIST:
557        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) {
558          if (p != node->pn_head) {          if (p == node->pn_head) {
559              output_expression(p, f, parenthesize_object_literals);
560            }
561            else {
562            Stream_printf(f, " %s ", get_op(node->pn_op));            Stream_printf(f, " %s ", get_op(node->pn_op));
563              output_expression(p, f, false);
564          }          }
         instrument_expression(p, f);  
565        }        }
566        break;        break;
567      default:      default:
# Line 386  Line 571 
571    case TOK_UNARYOP:    case TOK_UNARYOP:
572      switch (node->pn_op) {      switch (node->pn_op) {
573      case JSOP_NEG:      case JSOP_NEG:
574        Stream_write_char(f, '-');        Stream_write_string(f, "- ");
575        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, false);
576        break;        break;
577      case JSOP_POS:      case JSOP_POS:
578        Stream_write_char(f, '+');        Stream_write_string(f, "+ ");
579        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, false);
580        break;        break;
581      case JSOP_NOT:      case JSOP_NOT:
582        Stream_write_char(f, '!');        Stream_write_string(f, "! ");
583        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, false);
584        break;        break;
585      case JSOP_BITNOT:      case JSOP_BITNOT:
586        Stream_write_char(f, '~');        Stream_write_string(f, "~ ");
587        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, false);
588        break;        break;
589      case JSOP_TYPEOF:      case JSOP_TYPEOF:
590        Stream_write_string(f, "typeof ");        Stream_write_string(f, "typeof ");
591        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, false);
592        break;        break;
593      case JSOP_VOID:      case JSOP_VOID:
594        Stream_write_string(f, "void ");        Stream_write_string(f, "void ");
595        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, false);
596        break;        break;
597      default:      default:
598        abort();        fatal_source(file_id, node->pn_pos.begin.lineno, "unknown operator (%d)", node->pn_op);
599        break;        break;
600      }      }
601      break;      break;
# Line 424  Line 609 
609      case JSOP_INCPROP:      case JSOP_INCPROP:
610      case JSOP_INCELEM:      case JSOP_INCELEM:
611        Stream_write_string(f, "++");        Stream_write_string(f, "++");
612        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, false);
613        break;        break;
614      case JSOP_DECNAME:      case JSOP_DECNAME:
615      case JSOP_DECPROP:      case JSOP_DECPROP:
616      case JSOP_DECELEM:      case JSOP_DECELEM:
617        Stream_write_string(f, "--");        Stream_write_string(f, "--");
618        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, false);
619        break;        break;
620      case JSOP_NAMEINC:      case JSOP_NAMEINC:
621      case JSOP_PROPINC:      case JSOP_PROPINC:
622      case JSOP_ELEMINC:      case JSOP_ELEMINC:
623        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, parenthesize_object_literals);
624        Stream_write_string(f, "++");        Stream_write_string(f, "++");
625        break;        break;
626      case JSOP_NAMEDEC:      case JSOP_NAMEDEC:
627      case JSOP_PROPDEC:      case JSOP_PROPDEC:
628      case JSOP_ELEMDEC:      case JSOP_ELEMDEC:
629        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, parenthesize_object_literals);
630        Stream_write_string(f, "--");        Stream_write_string(f, "--");
631        break;        break;
632      default:      default:
# Line 455  Line 640 
640      break;      break;
641    case TOK_DELETE:    case TOK_DELETE:
642      Stream_write_string(f, "delete ");      Stream_write_string(f, "delete ");
643      instrument_expression(node->pn_kid, f);      output_expression(node->pn_kid, f, false);
644      break;      break;
645    case TOK_DOT:    case TOK_DOT:
646        /* numeric literals must be parenthesized */
647        switch (node->pn_expr->pn_type) {
648        case TOK_NUMBER:
649          Stream_write_char(f, '(');
650          output_expression(node->pn_expr, f, false);
651          Stream_write_char(f, ')');
652          break;
653        default:
654          output_expression(node->pn_expr, f, true);
655          break;
656        }
657      /*      /*
658      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'
659      contains illegal characters, we have to use the subscript syntax instead of      contains illegal characters, we have to use the subscript syntax instead of
660      the dot syntax.      the dot syntax.
661      */      */
     instrument_expression(node->pn_expr, f);  
662      assert(ATOM_IS_STRING(node->pn_atom));      assert(ATOM_IS_STRING(node->pn_atom));
663      {      {
664        JSString * s = ATOM_TO_STRING(node->pn_atom);        JSString * s = ATOM_TO_STRING(node->pn_atom);
665        /* XXX - semantics changed in 1.7 */        bool must_quote;
666        if (! ATOM_KEYWORD(node->pn_atom) && js_IsIdentifier(s)) {        if (JSSTRING_LENGTH(s) == 0) {
667          Stream_write_char(f, '.');          must_quote = true;
668          print_string_atom(node->pn_atom, f);        }
669          else if (js_CheckKeyword(JSSTRING_CHARS(s), JSSTRING_LENGTH(s)) != TOK_EOF) {
670            must_quote = true;
671          }
672          else if (! js_IsIdentifier(s)) {
673            must_quote = true;
674        }        }
675        else {        else {
676            must_quote = false;
677          }
678          if (must_quote) {
679          Stream_write_char(f, '[');          Stream_write_char(f, '[');
680          print_quoted_string_atom(node->pn_atom, f);          print_quoted_string_atom(node->pn_atom, f);
681          Stream_write_char(f, ']');          Stream_write_char(f, ']');
682        }        }
683          else {
684            Stream_write_char(f, '.');
685            print_string_atom(node->pn_atom, f);
686          }
687      }      }
688      break;      break;
689    case TOK_LB:    case TOK_LB:
690      instrument_expression(node->pn_left, f);      output_expression(node->pn_left, f, false);
691      Stream_write_char(f, '[');      Stream_write_char(f, '[');
692      instrument_expression(node->pn_right, f);      output_expression(node->pn_right, f, false);
693      Stream_write_char(f, ']');      Stream_write_char(f, ']');
694      break;      break;
695    case TOK_LP:    case TOK_LP:
# Line 496  Line 703 
703        }        }
704        /* TOK_COMMA is a special case: a hole in the array */        /* TOK_COMMA is a special case: a hole in the array */
705        if (p->pn_type != TOK_COMMA) {        if (p->pn_type != TOK_COMMA) {
706          instrument_expression(p, f);          output_expression(p, f, false);
707        }        }
708      }      }
709      if (node->pn_extra == PNX_ENDCOMMA) {      if (node->pn_extra == PNX_ENDCOMMA) {
# Line 505  Line 712 
712      Stream_write_char(f, ']');      Stream_write_char(f, ']');
713      break;      break;
714    case TOK_RC:    case TOK_RC:
715        if (parenthesize_object_literals) {
716          Stream_write_char(f, '(');
717        }
718      Stream_write_char(f, '{');      Stream_write_char(f, '{');
719      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) {
720        assert(p->pn_type == TOK_COLON);        if (p->pn_type != TOK_COLON) {
721            fatal_source(file_id, p->pn_pos.begin.lineno, "unsupported node type (%d)", p->pn_type);
722          }
723        if (p != node->pn_head) {        if (p != node->pn_head) {
724          Stream_write_string(f, ", ");          Stream_write_string(f, ", ");
725        }        }
# Line 515  Line 727 
727        /* check whether this is a getter or setter */        /* check whether this is a getter or setter */
728        switch (p->pn_op) {        switch (p->pn_op) {
729        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;  
730        case JSOP_SETTER:        case JSOP_SETTER:
731          Stream_write_string(f, "set ");          if (p->pn_op == JSOP_GETTER) {
732          instrument_expression(p->pn_left, f);            Stream_write_string(f, "get ");
733            }
734            else {
735              Stream_write_string(f, "set ");
736            }
737            output_expression(p->pn_left, f, false);
738            Stream_write_char(f, ' ');
739            if (p->pn_right->pn_type != TOK_FUNCTION) {
740              fatal_source(file_id, p->pn_pos.begin.lineno, "expected function");
741            }
742          instrument_function(p->pn_right, f, 0, FUNCTION_GETTER_OR_SETTER);          instrument_function(p->pn_right, f, 0, FUNCTION_GETTER_OR_SETTER);
743          break;          break;
744        default:        default:
745          instrument_expression(p->pn_left, f);          output_expression(p->pn_left, f, false);
746          Stream_write_string(f, ": ");          Stream_write_string(f, ": ");
747          instrument_expression(p->pn_right, f);          output_expression(p->pn_right, f, false);
748          break;          break;
749        }        }
750      }      }
751      Stream_write_char(f, '}');      Stream_write_char(f, '}');
752        if (parenthesize_object_literals) {
753          Stream_write_char(f, ')');
754        }
755      break;      break;
756    case TOK_RP:    case TOK_RP:
757      Stream_write_char(f, '(');      Stream_write_char(f, '(');
758      instrument_expression(node->pn_kid, f);      output_expression(node->pn_kid, f, false);
759      Stream_write_char(f, ')');      Stream_write_char(f, ')');
760      break;      break;
761    case TOK_NAME:    case TOK_NAME:
762      print_string_atom(node->pn_atom, f);      print_string_atom(node->pn_atom, f);
763        if (node->pn_expr != NULL) {
764          Stream_write_string(f, " = ");
765          output_expression(node->pn_expr, f, false);
766        }
767      break;      break;
768    case TOK_STRING:    case TOK_STRING:
769      print_quoted_string_atom(node->pn_atom, f);      print_quoted_string_atom(node->pn_atom, f);
770      break;      break;
771    case TOK_OBJECT:    case TOK_REGEXP:
772      switch (node->pn_op) {      assert(node->pn_op == JSOP_REGEXP);
773      case JSOP_OBJECT:      {
774        /* I assume this is JSOP_REGEXP */        JSObject * object = node->pn_pob->object;
775        abort();        jsval result;
776        break;        js_regexp_toString(context, object, &result);
777      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;  
778      }      }
779      break;      break;
780    case TOK_NUMBER:    case TOK_NUMBER:
# Line 572  Line 785 
785      To keep the output simple, special-case zero.      To keep the output simple, special-case zero.
786      */      */
787      if (node->pn_dval == 0.0) {      if (node->pn_dval == 0.0) {
788        Stream_write_string(f, "0");        if (signbit(node->pn_dval)) {
789            Stream_write_string(f, "-0");
790          }
791          else {
792            Stream_write_string(f, "0");
793          }
794        }
795        else if (node->pn_dval == INFINITY) {
796          Stream_write_string(f, "Number.POSITIVE_INFINITY");
797        }
798        else if (node->pn_dval == -INFINITY) {
799          Stream_write_string(f, "Number.NEGATIVE_INFINITY");
800        }
801        else if (isnan(node->pn_dval)) {
802          Stream_write_string(f, "Number.NaN");
803      }      }
804      else {      else {
805        Stream_printf(f, "%.15g", node->pn_dval);        Stream_printf(f, "%.15g", node->pn_dval);
# Line 598  Line 825 
825      }      }
826      break;      break;
827    case TOK_INSTANCEOF:    case TOK_INSTANCEOF:
828      instrument_expression(node->pn_left, f);      output_expression(node->pn_left, f, parenthesize_object_literals);
829      Stream_write_string(f, " instanceof ");      Stream_write_string(f, " instanceof ");
830      instrument_expression(node->pn_right, f);      output_expression(node->pn_right, f, false);
831      break;      break;
832    case TOK_IN:    case TOK_IN:
833      instrument_expression(node->pn_left, f);      output_expression(node->pn_left, f, false);
834      Stream_write_string(f, " in ");      Stream_write_string(f, " in ");
835      instrument_expression(node->pn_right, f);      output_expression(node->pn_right, f, false);
836      break;      break;
837    default:    case TOK_LEXICALSCOPE:
838      fatal("unsupported node type in file %s: %d", file_id, node->pn_type);      assert(node->pn_arity == PN_NAME);
839    }      assert(node->pn_expr->pn_type == TOK_LET);
840  }      assert(node->pn_expr->pn_arity == PN_BINARY);
841        Stream_write_string(f, "let(");
842  static void instrument_var_statement(JSParseNode * node, Stream * f, int indent) {      assert(node->pn_expr->pn_left->pn_type == TOK_LP);
843    assert(node->pn_arity == PN_LIST);      assert(node->pn_expr->pn_left->pn_arity == PN_LIST);
844    Stream_printf(f, "%*s", indent, "");      instrument_declarations(node->pn_expr->pn_left, f);
845    Stream_write_string(f, "var ");      Stream_write_string(f, ") ");
846    for (struct JSParseNode * p = node->pn_u.list.head; p != NULL; p = p->pn_next) {      output_expression(node->pn_expr->pn_right, f, true);
847      assert(p->pn_type == TOK_NAME);      break;
848      assert(p->pn_arity == PN_NAME);    case TOK_YIELD:
849      if (p != node->pn_head) {      assert(node->pn_arity == PN_UNARY);
850        Stream_write_string(f, ", ");      Stream_write_string(f, "yield");
851        if (node->pn_kid != NULL) {
852          Stream_write_char(f, ' ');
853          output_expression(node->pn_kid, f, true);
854      }      }
855      print_string_atom(p->pn_atom, f);      break;
856      if (p->pn_expr != NULL) {    case TOK_ARRAYCOMP:
857        Stream_write_string(f, " = ");      assert(node->pn_arity == PN_LIST);
858        instrument_expression(p->pn_expr, f);      {
859          JSParseNode * block_node;
860          switch (node->pn_count) {
861          case 1:
862            block_node = node->pn_head;
863            break;
864          case 2:
865            block_node = node->pn_head->pn_next;
866            break;
867          default:
868            abort();
869            break;
870          }
871          Stream_write_char(f, '[');
872          output_array_comprehension_or_generator_expression(block_node, f);
873          Stream_write_char(f, ']');
874      }      }
875        break;
876      case TOK_VAR:
877        assert(node->pn_arity == PN_LIST);
878        Stream_write_string(f, "var ");
879        instrument_declarations(node, f);
880        break;
881      case TOK_LET:
882        assert(node->pn_arity == PN_LIST);
883        Stream_write_string(f, "let ");
884        instrument_declarations(node, f);
885        break;
886      default:
887        fatal_source(file_id, node->pn_pos.begin.lineno, "unsupported node type (%d)", node->pn_type);
888    }    }
889  }  }
890    
# Line 634  Line 892 
892    switch (node->pn_type) {    switch (node->pn_type) {
893    case TOK_FUNCTION:    case TOK_FUNCTION:
894      instrument_function(node, f, indent, FUNCTION_NORMAL);      instrument_function(node, f, indent, FUNCTION_NORMAL);
895        Stream_write_char(f, '\n');
896      break;      break;
897    case TOK_LC:    case TOK_LC:
898      assert(node->pn_arity == PN_LIST);      assert(node->pn_arity == PN_LIST);
# Line 655  Line 914 
914      uint16_t line = node->pn_pos.begin.lineno;      uint16_t line = node->pn_pos.begin.lineno;
915      if (! is_jscoverage_if) {      if (! is_jscoverage_if) {
916        if (line > num_lines) {        if (line > num_lines) {
917          fatal("%s: script contains more than 65,535 lines", file_id);          fatal("file %s contains more than 65,535 lines", file_id);
918        }        }
919        if (line >= 2 && exclusive_directives[line - 2]) {        if (line >= 2 && exclusive_directives[line - 2]) {
920          is_jscoverage_if = true;          is_jscoverage_if = true;
# Line 664  Line 923 
923    
924      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
925      Stream_write_string(f, "if (");      Stream_write_string(f, "if (");
926      instrument_expression(node->pn_kid1, f);      output_expression(node->pn_kid1, f, false);
927      Stream_write_string(f, ") {\n");      Stream_write_string(f, ") {\n");
928      if (is_jscoverage_if && node->pn_kid3) {      if (is_jscoverage_if && node->pn_kid3) {
929        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 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, "switch (");      Stream_write_string(f, "switch (");
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      for (struct JSParseNode * p = node->pn_right->pn_head; p != NULL; p = p->pn_next) {      {
966        Stream_printf(f, "%*s", indent, "");        JSParseNode * list = node->pn_right;
967        switch (p->pn_type) {        if (list->pn_type == TOK_LEXICALSCOPE) {
968        case TOK_CASE:          list = list->pn_expr;
969          Stream_write_string(f, "case ");        }
970          instrument_expression(p->pn_left, f);        for (struct JSParseNode * p = list->pn_head; p != NULL; p = p->pn_next) {
971          Stream_write_string(f, ":\n");          Stream_printf(f, "%*s", indent, "");
972          break;          switch (p->pn_type) {
973        case TOK_DEFAULT:          case TOK_CASE:
974          Stream_write_string(f, "default:\n");            Stream_write_string(f, "case ");
975          break;            output_expression(p->pn_left, f, false);
976        default:            Stream_write_string(f, ":\n");
977          abort();            break;
978          break;          case TOK_DEFAULT:
979              Stream_write_string(f, "default:\n");
980              break;
981            default:
982              abort();
983              break;
984            }
985            instrument_statement(p->pn_right, f, indent + 2, false);
986        }        }
       instrument_statement(p->pn_right, f, indent + 2, false);  
987      }      }
988      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
989      Stream_write_string(f, "}\n");      Stream_write_string(f, "}\n");
# Line 731  Line 996 
996      assert(node->pn_arity == PN_BINARY);      assert(node->pn_arity == PN_BINARY);
997      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
998      Stream_write_string(f, "while (");      Stream_write_string(f, "while (");
999      instrument_expression(node->pn_left, f);      output_expression(node->pn_left, f, false);
1000      Stream_write_string(f, ") {\n");      Stream_write_string(f, ") {\n");
1001      instrument_statement(node->pn_right, f, indent + 2, false);      instrument_statement(node->pn_right, f, indent + 2, false);
1002      Stream_write_string(f, "}\n");      Stream_write_string(f, "}\n");
# Line 744  Line 1009 
1009      Stream_write_string(f, "}\n");      Stream_write_string(f, "}\n");
1010      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
1011      Stream_write_string(f, "while (");      Stream_write_string(f, "while (");
1012      instrument_expression(node->pn_right, f);      output_expression(node->pn_right, f, false);
1013      Stream_write_string(f, ");\n");      Stream_write_string(f, ");\n");
1014      break;      break;
1015    case TOK_FOR:    case TOK_FOR:
1016      assert(node->pn_arity == PN_BINARY);      assert(node->pn_arity == PN_BINARY);
1017      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
     Stream_write_string(f, "for (");  
1018      switch (node->pn_left->pn_type) {      switch (node->pn_left->pn_type) {
1019      case TOK_IN:      case TOK_IN:
1020        /* for/in */        /* for/in */
1021        assert(node->pn_left->pn_arity == PN_BINARY);        assert(node->pn_left->pn_arity == PN_BINARY);
1022        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);  
1023        break;        break;
1024      case TOK_RESERVED:      case TOK_FORHEAD:
1025        /* for (;;) */        /* for (;;) */
1026        assert(node->pn_left->pn_arity == PN_TERNARY);        assert(node->pn_left->pn_arity == PN_TERNARY);
1027          Stream_write_string(f, "for (");
1028        if (node->pn_left->pn_kid1) {        if (node->pn_left->pn_kid1) {
1029          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);  
         }  
1030        }        }
1031        Stream_write_string(f, ";");        Stream_write_string(f, ";");
1032        if (node->pn_left->pn_kid2) {        if (node->pn_left->pn_kid2) {
1033          Stream_write_char(f, ' ');          Stream_write_char(f, ' ');
1034          instrument_expression(node->pn_left->pn_kid2, f);          output_expression(node->pn_left->pn_kid2, f, false);
1035        }        }
1036        Stream_write_string(f, ";");        Stream_write_string(f, ";");
1037        if (node->pn_left->pn_kid3) {        if (node->pn_left->pn_kid3) {
1038          Stream_write_char(f, ' ');          Stream_write_char(f, ' ');
1039          instrument_expression(node->pn_left->pn_kid3, f);          output_expression(node->pn_left->pn_kid3, f, false);
1040        }        }
1041          Stream_write_char(f, ')');
1042        break;        break;
1043      default:      default:
1044        abort();        abort();
1045        break;        break;
1046      }      }
1047      Stream_write_string(f, ") {\n");      Stream_write_string(f, " {\n");
1048      instrument_statement(node->pn_right, f, indent + 2, false);      instrument_statement(node->pn_right, f, indent + 2, false);
1049      Stream_write_string(f, "}\n");      Stream_write_string(f, "}\n");
1050      break;      break;
# Line 810  Line 1052 
1052      assert(node->pn_arity == PN_UNARY);      assert(node->pn_arity == PN_UNARY);
1053      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
1054      Stream_write_string(f, "throw ");      Stream_write_string(f, "throw ");
1055      instrument_expression(node->pn_u.unary.kid, f);      output_expression(node->pn_u.unary.kid, f, false);
1056      Stream_write_string(f, ";\n");      Stream_write_string(f, ";\n");
1057      break;      break;
1058    case TOK_TRY:    case TOK_TRY:
# Line 819  Line 1061 
1061      instrument_statement(node->pn_kid1, f, indent + 2, false);      instrument_statement(node->pn_kid1, f, indent + 2, false);
1062      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
1063      Stream_write_string(f, "}\n");      Stream_write_string(f, "}\n");
1064      {      if (node->pn_kid2) {
1065        for (JSParseNode * catch = node->pn_kid2; catch != NULL; catch = catch->pn_kid2) {        assert(node->pn_kid2->pn_type == TOK_RESERVED);
1066          assert(catch->pn_type == TOK_CATCH);        for (JSParseNode * scope = node->pn_kid2->pn_head; scope != NULL; scope = scope->pn_next) {
1067            assert(scope->pn_type == TOK_LEXICALSCOPE);
1068            JSParseNode * catch_node = scope->pn_expr;
1069            assert(catch_node->pn_type == TOK_CATCH);
1070          Stream_printf(f, "%*s", indent, "");          Stream_printf(f, "%*s", indent, "");
1071          Stream_write_string(f, "catch (");          Stream_write_string(f, "catch (");
1072          assert(catch->pn_kid1->pn_arity == PN_NAME);          output_expression(catch_node->pn_kid1, f, false);
1073          print_string_atom(catch->pn_kid1->pn_atom, f);          if (catch_node->pn_kid2) {
         if (catch->pn_kid1->pn_expr) {  
1074            Stream_write_string(f, " if ");            Stream_write_string(f, " if ");
1075            instrument_expression(catch->pn_kid1->pn_expr, f);            output_expression(catch_node->pn_kid2, f, false);
1076          }          }
1077          Stream_write_string(f, ") {\n");          Stream_write_string(f, ") {\n");
1078          instrument_statement(catch->pn_kid3, f, indent + 2, false);          instrument_statement(catch_node->pn_kid3, f, indent + 2, false);
1079          Stream_printf(f, "%*s", indent, "");          Stream_printf(f, "%*s", indent, "");
1080          Stream_write_string(f, "}\n");          Stream_write_string(f, "}\n");
1081        }        }
# Line 852  Line 1096 
1096      assert(node->pn_arity == PN_NAME || node->pn_arity == PN_NULLARY);      assert(node->pn_arity == PN_NAME || node->pn_arity == PN_NULLARY);
1097      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
1098      Stream_write_string(f, node->pn_type == TOK_BREAK? "break": "continue");      Stream_write_string(f, node->pn_type == TOK_BREAK? "break": "continue");
1099      JSAtom * atom = node->pn_u.name.atom;      if (node->pn_atom != NULL) {
     if (atom != NULL) {  
1100        Stream_write_char(f, ' ');        Stream_write_char(f, ' ');
1101        print_string_atom(node->pn_atom, f);        print_string_atom(node->pn_atom, f);
1102      }      }
# Line 863  Line 1106 
1106      assert(node->pn_arity == PN_BINARY);      assert(node->pn_arity == PN_BINARY);
1107      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
1108      Stream_write_string(f, "with (");      Stream_write_string(f, "with (");
1109      instrument_expression(node->pn_left, f);      output_expression(node->pn_left, f, false);
1110      Stream_write_string(f, ") {\n");      Stream_write_string(f, ") {\n");
1111      instrument_statement(node->pn_right, f, indent + 2, false);      instrument_statement(node->pn_right, f, indent + 2, false);
1112      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
1113      Stream_write_string(f, "}\n");      Stream_write_string(f, "}\n");
1114      break;      break;
1115    case TOK_VAR:    case TOK_VAR:
1116      instrument_var_statement(node, f, indent);      Stream_printf(f, "%*s", indent, "");
1117        output_expression(node, f, false);
1118      Stream_write_string(f, ";\n");      Stream_write_string(f, ";\n");
1119      break;      break;
1120    case TOK_RETURN:    case TOK_RETURN:
# Line 879  Line 1123 
1123      Stream_write_string(f, "return");      Stream_write_string(f, "return");
1124      if (node->pn_kid != NULL) {      if (node->pn_kid != NULL) {
1125        Stream_write_char(f, ' ');        Stream_write_char(f, ' ');
1126        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, true);
1127      }      }
1128      Stream_write_string(f, ";\n");      Stream_write_string(f, ";\n");
1129      break;      break;
# Line 887  Line 1131 
1131      assert(node->pn_arity == PN_UNARY);      assert(node->pn_arity == PN_UNARY);
1132      Stream_printf(f, "%*s", indent, "");      Stream_printf(f, "%*s", indent, "");
1133      if (node->pn_kid != NULL) {      if (node->pn_kid != NULL) {
1134        instrument_expression(node->pn_kid, f);        output_expression(node->pn_kid, f, true);
1135      }      }
1136      Stream_write_string(f, ";\n");      Stream_write_string(f, ";\n");
1137      break;      break;
1138    case TOK_COLON:    case TOK_COLON:
1139      {
1140      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 ...  
     */  
1141      Stream_printf(f, "%*s", indent < 2? 0: indent - 2, "");      Stream_printf(f, "%*s", indent < 2? 0: indent - 2, "");
1142      print_string_atom(node->pn_atom, f);      print_string_atom(node->pn_atom, f);
1143      Stream_write_string(f, ":\n");      Stream_write_string(f, ":\n");
1144        JSParseNode * labelled = node->pn_expr;
1145        if (labelled->pn_type == TOK_LEXICALSCOPE) {
1146          labelled = labelled->pn_expr;
1147        }
1148        if (labelled->pn_type == TOK_LC) {
1149          /* labelled block */
1150          Stream_printf(f, "%*s", indent, "");
1151          Stream_write_string(f, "{\n");
1152          instrument_statement(labelled, f, indent + 2, false);
1153          Stream_printf(f, "%*s", indent, "");
1154          Stream_write_string(f, "}\n");
1155        }
1156        else {
1157          /*
1158          This one is tricky: can't output instrumentation between the label and the
1159          statement it's supposed to label, so use output_statement instead of
1160          instrument_statement.
1161          */
1162          output_statement(labelled, f, indent, false);
1163        }
1164        break;
1165      }
1166      case TOK_LEXICALSCOPE:
1167        /* let statement */
1168        assert(node->pn_arity == PN_NAME);
1169        switch (node->pn_expr->pn_type) {
1170        case TOK_LET:
1171          /* let statement */
1172          assert(node->pn_expr->pn_arity == PN_BINARY);
1173          instrument_statement(node->pn_expr, f, indent, false);
1174          break;
1175        case TOK_LC:
1176          /* block */
1177          Stream_printf(f, "%*s", indent, "");
1178          Stream_write_string(f, "{\n");
1179          instrument_statement(node->pn_expr, f, indent + 2, false);
1180          Stream_printf(f, "%*s", indent, "");
1181          Stream_write_string(f, "}\n");
1182          break;
1183        case TOK_FOR:
1184          instrument_statement(node->pn_expr, f, indent, false);
1185          break;
1186        default:
1187          abort();
1188          break;
1189        }
1190        break;
1191      case TOK_LET:
1192        switch (node->pn_arity) {
1193        case PN_BINARY:
1194          /* let statement */
1195          Stream_printf(f, "%*s", indent, "");
1196          Stream_write_string(f, "let (");
1197          assert(node->pn_left->pn_type == TOK_LP);
1198          assert(node->pn_left->pn_arity == PN_LIST);
1199          instrument_declarations(node->pn_left, f);
1200          Stream_write_string(f, ") {\n");
1201          instrument_statement(node->pn_right, f, indent + 2, false);
1202          Stream_printf(f, "%*s", indent, "");
1203          Stream_write_string(f, "}\n");
1204          break;
1205        case PN_LIST:
1206          /* let definition */
1207          Stream_printf(f, "%*s", indent, "");
1208          output_expression(node, f, false);
1209          Stream_write_string(f, ";\n");
1210          break;
1211        default:
1212          abort();
1213          break;
1214        }
1215        break;
1216      case TOK_DEBUGGER:
1217        Stream_printf(f, "%*s", indent, "");
1218        Stream_write_string(f, "debugger;\n");
1219        break;
1220      case TOK_SEQ:
1221      /*      /*
1222      ... use output_statement instead of instrument_statement.      This occurs with the statement:
1223        for (var a = b in c) {}
1224      */      */
1225      output_statement(node->pn_expr, f, indent, false);      assert(node->pn_arity == PN_LIST);
1226        for (JSParseNode * p = node->pn_head; p != NULL; p = p->pn_next) {
1227          instrument_statement(p, f, indent, false);
1228        }
1229      break;      break;
1230    default:    default:
1231      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);
1232    }    }
1233  }  }
1234    
# Line 916  Line 1238 
1238  TOK_EXPORT, TOK_IMPORT are not handled.  TOK_EXPORT, TOK_IMPORT are not handled.
1239  */  */
1240  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) {
1241    if (node->pn_type != TOK_LC) {    if (node->pn_type != TOK_LC && node->pn_type != TOK_LEXICALSCOPE) {
1242      uint16_t line = node->pn_pos.begin.lineno;      uint16_t line = node->pn_pos.begin.lineno;
1243      if (line > num_lines) {      if (line > num_lines) {
1244        fatal("%s: script contains more than 65,535 lines", file_id);        fatal("file %s contains more than 65,535 lines", file_id);
1245      }      }
1246    
1247      /* the root node has line number 0 */      /* the root node has line number 0 */
# Line 966  Line 1288 
1288    return true;    return true;
1289  }  }
1290    
1291    static void error_reporter(JSContext * context, const char * message, JSErrorReport * report) {
1292      warn_source(file_id, report->lineno, "%s", message);
1293    }
1294    
1295  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) {
1296    file_id = id;    file_id = id;
1297    
   /* 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);  
   }  
   
1298    /* parse the javascript */    /* parse the javascript */
1299    JSParseNode * node = js_ParseTokenStream(context, global, token_stream);    JSParseContext parse_context;
1300      if (! js_InitParseContext(context, &parse_context, NULL, NULL, characters, num_characters, NULL, NULL, 1)) {
1301        fatal("cannot create token stream from file %s", file_id);
1302      }
1303      JSErrorReporter old_error_reporter = JS_SetErrorReporter(context, error_reporter);
1304      JSParseNode * node = js_ParseScript(context, global, &parse_context);
1305    if (node == NULL) {    if (node == NULL) {
1306      fatal("parse error in file: %s", file_id);      js_ReportUncaughtException(context);
1307        fatal("parse error in file %s", file_id);
1308    }    }
1309      JS_SetErrorReporter(context, old_error_reporter);
1310    num_lines = node->pn_pos.end.lineno;    num_lines = node->pn_pos.end.lineno;
1311    lines = xmalloc(num_lines);    lines = (char *) xmalloc(num_lines);
1312    for (unsigned int i = 0; i < num_lines; i++) {    for (unsigned int i = 0; i < num_lines; i++) {
1313      lines[i] = 0;      lines[i] = 0;
1314    }    }
# Line 998  Line 1325 
1325    size_t i = 0;    size_t i = 0;
1326    while (i < num_characters) {    while (i < num_characters) {
1327      if (line_number == UINT16_MAX) {      if (line_number == UINT16_MAX) {
1328        fatal("%s: script has more than 65,535 lines", file_id);        fatal("file %s contains more than 65,535 lines", file_id);
1329      }      }
1330      line_number++;      line_number++;
1331      size_t line_start = i;      size_t line_start = i;
# Line 1061  Line 1388 
1388    
1389    Stream * instrumented = Stream_new(0);    Stream * instrumented = Stream_new(0);
1390    instrument_statement(node, instrumented, 0, false);    instrument_statement(node, instrumented, 0, false);
1391      js_FinishParseContext(context, &parse_context);
1392    
1393    /* write line number info to the output */    /* write line number info to the output */
1394    Stream_write_string(output, "/* automatically generated by JSCoverage - do not edit */\n");    Stream_write_string(output, "/* automatically generated by JSCoverage - do not edit */\n");
1395    Stream_write_string(output, "if (! top._$jscoverage) {\n  top._$jscoverage = {};\n}\n");    if (jscoverage_mozilla) {
1396    Stream_write_string(output, "var _$jscoverage = top._$jscoverage;\n");      Stream_write_string(output, "try {\n");
1397        Stream_write_string(output, "  Components.utils.import('resource://gre/modules/jscoverage.jsm');\n");
1398        Stream_printf(output, "  dump('%s: successfully imported jscoverage module\\n');\n", id);
1399        Stream_write_string(output, "}\n");
1400        Stream_write_string(output, "catch (e) {\n");
1401        Stream_write_string(output, "  _$jscoverage = {};\n");
1402        Stream_printf(output, "  dump('%s: failed to import jscoverage module - coverage not available for this file\\n');\n", id);
1403        Stream_write_string(output, "}\n");
1404      }
1405      else {
1406        Stream_write_string(output, "if (! top._$jscoverage) {\n  top._$jscoverage = {};\n}\n");
1407        Stream_write_string(output, "var _$jscoverage = top._$jscoverage;\n");
1408      }
1409    Stream_printf(output, "if (! _$jscoverage['%s']) {\n", file_id);    Stream_printf(output, "if (! _$jscoverage['%s']) {\n", file_id);
1410    Stream_printf(output, "  _$jscoverage['%s'] = [];\n", file_id);    Stream_printf(output, "  _$jscoverage['%s'] = [];\n", file_id);
1411    for (int i = 0; i < num_lines; i++) {    for (int i = 0; i < num_lines; i++) {
# Line 1090  Line 1430 
1430    /* conditionals */    /* conditionals */
1431    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) {
1432      Stream_write_string(output, "if (!(");      Stream_write_string(output, "if (!(");
1433      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);  
       }  
     }  
1434      Stream_write_string(output, ")) {\n");      Stream_write_string(output, ")) {\n");
1435      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);
1436      Stream_write_string(output, "}\n");      Stream_write_string(output, "}\n");
# Line 1149  Line 1481 
1481            /* line feed (new line) */            /* line feed (new line) */
1482            done = true;            done = true;
1483            break;            break;
1484            /* IE doesn't support this */
1485            /*
1486          case 0xb:          case 0xb:
           /* vertical tab */  
1487            Stream_write_string(output, "\\v");            Stream_write_string(output, "\\v");
1488            break;            break;
1489            */
1490          case 0xc:          case 0xc:
1491            /* form feed */            /* form feed */
1492            Stream_write_string(output, "\\f");            Stream_write_string(output, "\\f");
# Line 1207  Line 1541 
1541            /* line feed (new line) */            /* line feed (new line) */
1542            done = true;            done = true;
1543            break;            break;
1544            /* IE doesn't support this */
1545            /*
1546          case 0xb:          case 0xb:
           /* vertical tab */  
1547            Stream_write_string(output, "\\v");            Stream_write_string(output, "\\v");
1548            break;            break;
1549            */
1550          case 0xc:          case 0xc:
1551            /* form feed */            /* form feed */
1552            Stream_write_string(output, "\\f");            Stream_write_string(output, "\\f");
# Line 1285  Line 1621 
1621  };  };
1622    
1623  static int compare_strings(const void * p1, const void * p2) {  static int compare_strings(const void * p1, const void * p2) {
1624    return strcmp(p1, p2) == 0;    return strcmp((const char *) p1, (const char *) p2) == 0;
1625  }  }
1626    
1627  Coverage * Coverage_new(void) {  Coverage * Coverage_new(void) {
1628    Coverage * result = xmalloc(sizeof(Coverage));    Coverage * result = (Coverage *) xmalloc(sizeof(Coverage));
1629    result->coverage_table = JS_NewHashTable(1024, JS_HashString, compare_strings, NULL, NULL, NULL);    result->coverage_table = JS_NewHashTable(1024, JS_HashString, compare_strings, NULL, NULL, NULL);
1630    if (result->coverage_table == NULL) {    if (result->coverage_table == NULL) {
1631      fatal("cannot create hash table");      fatal("cannot create hash table");
# Line 1324  Line 1660 
1660  };  };
1661    
1662  static intN enumerator(JSHashEntry * entry, intN i, void * arg) {  static intN enumerator(JSHashEntry * entry, intN i, void * arg) {
1663    struct EnumeratorArg * enumerator_arg = arg;    struct EnumeratorArg * enumerator_arg = (struct EnumeratorArg *) arg;
1664    enumerator_arg->f(entry->value, i, enumerator_arg->p);    enumerator_arg->f((FileCoverage *) entry->value, i, enumerator_arg->p);
1665    return 0;    return 0;
1666  }  }
1667    
# Line 1337  Line 1673 
1673  }  }
1674    
1675  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) {
1676      int result = 0;
1677    
1678    jschar * base = js_InflateString(context, (char *) json, &length);    jschar * base = js_InflateString(context, (char *) json, &length);
1679    if (base == NULL) {    if (base == NULL) {
1680      fatal("out of memory");      fatal("out of memory");
# Line 1349  Line 1687 
1687    
1688    JS_free(context, base);    JS_free(context, base);
1689    
1690    JSTokenStream * token_stream = js_NewTokenStream(context, parenthesized_json, length + 2, NULL, 1, NULL);    JSParseContext parse_context;
1691    if (token_stream == NULL) {    if (! js_InitParseContext(context, &parse_context, NULL, NULL, parenthesized_json, length + 2, NULL, NULL, 1)) {
1692      fatal("cannot create token stream");      free(parenthesized_json);
1693        return -1;
1694    }    }
1695      JSParseNode * root = js_ParseScript(context, global, &parse_context);
   JSParseNode * root = js_ParseTokenStream(context, global, token_stream);  
1696    free(parenthesized_json);    free(parenthesized_json);
1697    
1698      JSParseNode * semi = NULL;
1699      JSParseNode * object = NULL;
1700    
1701    if (root == NULL) {    if (root == NULL) {
1702      return -1;      result = -1;
1703        goto done;
1704    }    }
1705    
1706    /* root node must be TOK_LC */    /* root node must be TOK_LC */
1707    if (root->pn_type != TOK_LC) {    if (root->pn_type != TOK_LC) {
1708      return -1;      result = -1;
1709        goto done;
1710    }    }
1711    JSParseNode * semi = root->pn_u.list.head;    semi = root->pn_u.list.head;
1712    
1713    /* 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 */
1714    if (semi->pn_type != TOK_SEMI || semi->pn_next != NULL) {    if (semi->pn_type != TOK_SEMI || semi->pn_next != NULL) {
1715      return -1;      result = -1;
1716    }      goto done;
   JSParseNode * parenthesized = semi->pn_kid;  
   
   /* this must be a parenthesized expression */  
   if (parenthesized->pn_type != TOK_RP) {  
     return -1;  
1717    }    }
1718    JSParseNode * object = parenthesized->pn_kid;    object = semi->pn_kid;
1719    
1720    /* this must be an object literal */    /* this must be an object literal */
1721    if (object->pn_type != TOK_RC) {    if (object->pn_type != TOK_RC) {
1722      return -1;      result = -1;
1723        goto done;
1724    }    }
1725    
1726    for (JSParseNode * p = object->pn_head; p != NULL; p = p->pn_next) {    for (JSParseNode * p = object->pn_head; p != NULL; p = p->pn_next) {
1727      /* every element of this list must be TOK_COLON */      /* every element of this list must be TOK_COLON */
1728      if (p->pn_type != TOK_COLON) {      if (p->pn_type != TOK_COLON) {
1729        return -1;        result = -1;
1730          goto done;
1731      }      }
1732    
1733      /* the key must be a string representing the file */      /* the key must be a string representing the file */
1734      JSParseNode * key = p->pn_left;      JSParseNode * key = p->pn_left;
1735      if (key->pn_type != TOK_STRING || ! ATOM_IS_STRING(key->pn_atom)) {      if (key->pn_type != TOK_STRING || ! ATOM_IS_STRING(key->pn_atom)) {
1736        return -1;        result = -1;
1737          goto done;
1738      }      }
1739      char * id_bytes = JS_GetStringBytes(ATOM_TO_STRING(key->pn_atom));      char * id_bytes = JS_GetStringBytes(ATOM_TO_STRING(key->pn_atom));
1740    
1741      /* the value must be an object literal OR an array */      /* the value must be an object literal OR an array */
1742      JSParseNode * value = p->pn_right;      JSParseNode * value = p->pn_right;
1743      if (! (value->pn_type == TOK_RC || value->pn_type == TOK_RB)) {      if (! (value->pn_type == TOK_RC || value->pn_type == TOK_RB)) {
1744        return -1;        result = -1;
1745          goto done;
1746      }      }
1747    
1748      JSParseNode * array = NULL;      JSParseNode * array = NULL;
# Line 1411  Line 1754 
1754      else if (value->pn_type == TOK_RC) {      else if (value->pn_type == TOK_RC) {
1755        /* an object literal */        /* an object literal */
1756        if (value->pn_count != 2) {        if (value->pn_count != 2) {
1757          return -1;          result = -1;
1758            goto done;
1759        }        }
1760        for (JSParseNode * element = value->pn_head; element != NULL; element = element->pn_next) {        for (JSParseNode * element = value->pn_head; element != NULL; element = element->pn_next) {
1761          if (element->pn_type != TOK_COLON) {          if (element->pn_type != TOK_COLON) {
1762            return -1;            result = -1;
1763              goto done;
1764          }          }
1765          JSParseNode * left = element->pn_left;          JSParseNode * left = element->pn_left;
1766          if (left->pn_type != TOK_STRING || ! ATOM_IS_STRING(left->pn_atom)) {          if (left->pn_type != TOK_STRING || ! ATOM_IS_STRING(left->pn_atom)) {
1767            return -1;            result = -1;
1768              goto done;
1769          }          }
1770          const char * s = JS_GetStringBytes(ATOM_TO_STRING(left->pn_atom));          const char * s = JS_GetStringBytes(ATOM_TO_STRING(left->pn_atom));
1771          if (strcmp(s, "coverage") == 0) {          if (strcmp(s, "coverage") == 0) {
1772            array = element->pn_right;            array = element->pn_right;
1773            if (array->pn_type != TOK_RB) {            if (array->pn_type != TOK_RB) {
1774              return -1;              result = -1;
1775                goto done;
1776            }            }
1777          }          }
1778          else if (strcmp(s, "source") == 0) {          else if (strcmp(s, "source") == 0) {
1779            source = element->pn_right;            source = element->pn_right;
1780            if (source->pn_type != TOK_RB) {            if (source->pn_type != TOK_RB) {
1781              return -1;              result = -1;
1782                goto done;
1783            }            }
1784          }          }
1785          else {          else {
1786            return -1;            result = -1;
1787              goto done;
1788          }          }
1789        }        }
1790      }      }
1791      else {      else {
1792        return -1;        result = -1;
1793          goto done;
1794      }      }
1795    
1796      if (array == NULL) {      if (array == NULL) {
1797        return -1;        result = -1;
1798          goto done;
1799      }      }
1800    
1801      /* look up the file in the coverage table */      /* look up the file in the coverage table */
1802      FileCoverage * file_coverage = JS_HashTableLookup(coverage->coverage_table, id_bytes);      FileCoverage * file_coverage = (FileCoverage *) JS_HashTableLookup(coverage->coverage_table, id_bytes);
1803      if (file_coverage == NULL) {      if (file_coverage == NULL) {
1804        /* not there: create a new one */        /* not there: create a new one */
1805        char * id = xstrdup(id_bytes);        char * id = xstrdup(id_bytes);
1806        file_coverage = xmalloc(sizeof(FileCoverage));        file_coverage = (FileCoverage *) xmalloc(sizeof(FileCoverage));
1807        file_coverage->id = id;        file_coverage->id = id;
1808        file_coverage->num_coverage_lines = array->pn_count;        file_coverage->num_coverage_lines = array->pn_count;
1809        file_coverage->coverage_lines = xnew(int, array->pn_count);        file_coverage->coverage_lines = xnew(int, array->pn_count);
# Line 1468  Line 1819 
1819            file_coverage->coverage_lines[i] = -1;            file_coverage->coverage_lines[i] = -1;
1820          }          }
1821          else {          else {
1822            return -1;            result = -1;
1823              goto done;
1824          }          }
1825        }        }
1826        assert(i == array->pn_count);        assert(i == array->pn_count);
1827    
1828        /* add to the hash table */        /* add to the hash table */
1829        JS_HashTableAdd(coverage->coverage_table, id, file_coverage);        JS_HashTableAdd(coverage->coverage_table, id, file_coverage);
1830        struct FileCoverageList * coverage_list = xmalloc(sizeof(struct FileCoverageList));        struct FileCoverageList * coverage_list = (FileCoverageList *) xmalloc(sizeof(struct FileCoverageList));
1831        coverage_list->file_coverage = file_coverage;        coverage_list->file_coverage = file_coverage;
1832        coverage_list->next = coverage->coverage_list;        coverage_list->next = coverage->coverage_list;
1833        coverage->coverage_list = coverage_list;        coverage->coverage_list = coverage_list;
# Line 1484  Line 1836 
1836        /* sanity check */        /* sanity check */
1837        assert(strcmp(file_coverage->id, id_bytes) == 0);        assert(strcmp(file_coverage->id, id_bytes) == 0);
1838        if (file_coverage->num_coverage_lines != array->pn_count) {        if (file_coverage->num_coverage_lines != array->pn_count) {
1839          return -2;          result = -2;
1840            goto done;
1841        }        }
1842    
1843        /* merge the coverage */        /* merge the coverage */
# Line 1492  Line 1845 
1845        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++) {
1846          if (element->pn_type == TOK_NUMBER) {          if (element->pn_type == TOK_NUMBER) {
1847            if (file_coverage->coverage_lines[i] == -1) {            if (file_coverage->coverage_lines[i] == -1) {
1848              return -2;              result = -2;
1849                goto done;
1850            }            }
1851            file_coverage->coverage_lines[i] += (int) element->pn_dval;            file_coverage->coverage_lines[i] += (int) element->pn_dval;
1852          }          }
1853          else if (element->pn_type == TOK_PRIMARY && element->pn_op == JSOP_NULL) {          else if (element->pn_type == TOK_PRIMARY && element->pn_op == JSOP_NULL) {
1854            if (file_coverage->coverage_lines[i] != -1) {            if (file_coverage->coverage_lines[i] != -1) {
1855              return -2;              result = -2;
1856                goto done;
1857            }            }
1858          }          }
1859          else {          else {
1860            return -1;            result = -1;
1861              goto done;
1862          }          }
1863        }        }
1864        assert(i == array->pn_count);        assert(i == array->pn_count);
# Line 1515  Line 1871 
1871        uint32 i = 0;        uint32 i = 0;
1872        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++) {
1873          if (element->pn_type != TOK_STRING) {          if (element->pn_type != TOK_STRING) {
1874            return -1;            result = -1;
1875              goto done;
1876          }          }
1877          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)));
1878        }        }
# Line 1523  Line 1880 
1880      }      }
1881    }    }
1882    
1883    return 0;  done:
1884      js_FinishParseContext(context, &parse_context);
1885      return result;
1886  }  }

Legend:
Removed from v.245  
changed lines
  Added in v.402

  ViewVC Help
Powered by ViewVC 1.1.24