31 |
#include <jsexn.h> |
#include <jsexn.h> |
32 |
#include <jsfun.h> |
#include <jsfun.h> |
33 |
#include <jsinterp.h> |
#include <jsinterp.h> |
34 |
|
#include <jsiter.h> |
35 |
#include <jsparse.h> |
#include <jsparse.h> |
36 |
#include <jsregexp.h> |
#include <jsregexp.h> |
37 |
#include <jsscope.h> |
#include <jsscope.h> |
56 |
static JSRuntime * runtime = NULL; |
static JSRuntime * runtime = NULL; |
57 |
static JSContext * context = NULL; |
static JSContext * context = NULL; |
58 |
static JSObject * global = NULL; |
static JSObject * global = NULL; |
59 |
|
static JSVersion js_version = JSVERSION_ECMA_3; |
60 |
|
|
61 |
/* |
/* |
62 |
JSParseNode objects store line numbers starting from 1. |
JSParseNode objects store line numbers starting from 1. |
66 |
static char * lines = NULL; |
static char * lines = NULL; |
67 |
static uint16_t num_lines = 0; |
static uint16_t num_lines = 0; |
68 |
|
|
69 |
|
void jscoverage_set_js_version(const char * version) { |
70 |
|
js_version = atoi(version); |
71 |
|
} |
72 |
|
|
73 |
void jscoverage_init(void) { |
void jscoverage_init(void) { |
74 |
runtime = JS_NewRuntime(8L * 1024L * 1024L); |
runtime = JS_NewRuntime(8L * 1024L * 1024L); |
75 |
if (runtime == NULL) { |
if (runtime == NULL) { |
81 |
fatal("cannot create context"); |
fatal("cannot create context"); |
82 |
} |
} |
83 |
|
|
84 |
|
JS_SetVersion(context, js_version); |
85 |
|
|
86 |
global = JS_NewObject(context, NULL, NULL, NULL); |
global = JS_NewObject(context, NULL, NULL, NULL); |
87 |
if (global == NULL) { |
if (global == NULL) { |
88 |
fatal("cannot create global object"); |
fatal("cannot create global object"); |
257 |
|
|
258 |
static void instrument_expression(JSParseNode * node, Stream * f); |
static void instrument_expression(JSParseNode * node, Stream * f); |
259 |
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); |
260 |
|
static void output_statement(JSParseNode * node, Stream * f, int indent, bool is_jscoverage_if); |
261 |
|
|
262 |
enum FunctionType { |
enum FunctionType { |
263 |
FUNCTION_NORMAL, |
FUNCTION_NORMAL, |
264 |
FUNCTION_GETTER_OR_SETTER |
FUNCTION_GETTER_OR_SETTER |
265 |
}; |
}; |
266 |
|
|
267 |
|
static void output_for_in(JSParseNode * node, Stream * f) { |
268 |
|
assert(node->pn_type == TOK_FOR); |
269 |
|
assert(node->pn_arity == PN_BINARY); |
270 |
|
Stream_write_string(f, "for "); |
271 |
|
if (node->pn_iflags & JSITER_FOREACH) { |
272 |
|
Stream_write_string(f, "each "); |
273 |
|
} |
274 |
|
Stream_write_char(f, '('); |
275 |
|
instrument_expression(node->pn_left, f); |
276 |
|
Stream_write_char(f, ')'); |
277 |
|
} |
278 |
|
|
279 |
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) { |
280 |
assert(node->pn_type == TOK_FUNCTION); |
assert(node->pn_type == TOK_FUNCTION); |
281 |
assert(node->pn_arity == PN_FUNC); |
assert(node->pn_arity == PN_FUNC); |
320 |
Stream_write_string(f, ") {\n"); |
Stream_write_string(f, ") {\n"); |
321 |
|
|
322 |
/* function body */ |
/* function body */ |
323 |
instrument_statement(node->pn_body, f, indent + 2, false); |
if (function->flags & JSFUN_EXPR_CLOSURE) { |
324 |
|
/* expression closure */ |
325 |
|
output_statement(node->pn_body, f, indent + 2, false); |
326 |
|
} |
327 |
|
else { |
328 |
|
instrument_statement(node->pn_body, f, indent + 2, false); |
329 |
|
} |
330 |
|
|
331 |
Stream_write_string(f, "}\n"); |
Stream_write_string(f, "}\n"); |
332 |
} |
} |
333 |
|
|
334 |
static void instrument_function_call(JSParseNode * node, Stream * f) { |
static void instrument_function_call(JSParseNode * node, Stream * f) { |
335 |
instrument_expression(node->pn_head, f); |
if (node->pn_head->pn_type == TOK_FUNCTION) { |
336 |
Stream_write_char(f, '('); |
/* it's a generator expression */ |
337 |
for (struct JSParseNode * p = node->pn_head->pn_next; p != NULL; p = p->pn_next) { |
JSParseNode * function_node = node->pn_head; |
338 |
if (p != node->pn_head->pn_next) { |
JSParseNode * lexical_scope_node = function_node->pn_body; |
339 |
Stream_write_string(f, ", "); |
assert(lexical_scope_node->pn_type == TOK_LEXICALSCOPE); |
340 |
|
assert(lexical_scope_node->pn_arity == PN_NAME); |
341 |
|
JSParseNode * for_node = lexical_scope_node->pn_body; |
342 |
|
assert(for_node->pn_type == TOK_FOR); |
343 |
|
assert(for_node->pn_arity == PN_BINARY); |
344 |
|
JSParseNode * if_node = NULL; |
345 |
|
JSParseNode * semi_node; |
346 |
|
switch (for_node->pn_right->pn_type) { |
347 |
|
case TOK_SEMI: |
348 |
|
semi_node = for_node->pn_right; |
349 |
|
break; |
350 |
|
case TOK_IF: |
351 |
|
if_node = for_node->pn_right; |
352 |
|
assert(if_node->pn_arity == PN_TERNARY); |
353 |
|
semi_node = if_node->pn_kid2; |
354 |
|
assert(semi_node->pn_type == TOK_SEMI); |
355 |
|
break; |
356 |
|
default: |
357 |
|
abort(); |
358 |
|
break; |
359 |
|
} |
360 |
|
assert(semi_node->pn_arity == PN_UNARY); |
361 |
|
JSParseNode * yield_node = semi_node->pn_kid; |
362 |
|
assert(yield_node->pn_type == TOK_YIELD); |
363 |
|
Stream_write_char(f, '('); |
364 |
|
instrument_expression(yield_node->pn_kid, f); |
365 |
|
Stream_write_char(f, ' '); |
366 |
|
output_for_in(for_node, f); |
367 |
|
if (if_node) { |
368 |
|
Stream_write_string(f, " if ("); |
369 |
|
instrument_expression(if_node->pn_kid1, f); |
370 |
|
Stream_write_char(f, ')'); |
371 |
|
} |
372 |
|
Stream_write_char(f, ')'); |
373 |
|
} |
374 |
|
else { |
375 |
|
instrument_expression(node->pn_head, f); |
376 |
|
Stream_write_char(f, '('); |
377 |
|
for (struct JSParseNode * p = node->pn_head->pn_next; p != NULL; p = p->pn_next) { |
378 |
|
if (p != node->pn_head->pn_next) { |
379 |
|
Stream_write_string(f, ", "); |
380 |
|
} |
381 |
|
instrument_expression(p, f); |
382 |
|
} |
383 |
|
Stream_write_char(f, ')'); |
384 |
|
} |
385 |
|
} |
386 |
|
|
387 |
|
static void instrument_declarations(JSParseNode * list, Stream * f) { |
388 |
|
assert(list->pn_arity == PN_LIST); |
389 |
|
for (JSParseNode * p = list->pn_head; p != NULL; p = p->pn_next) { |
390 |
|
switch (p->pn_type) { |
391 |
|
case TOK_NAME: |
392 |
|
assert(p->pn_arity == PN_NAME); |
393 |
|
if (p != list->pn_head) { |
394 |
|
Stream_write_string(f, ", "); |
395 |
|
} |
396 |
|
print_string_atom(p->pn_atom, f); |
397 |
|
if (p->pn_expr != NULL) { |
398 |
|
Stream_write_string(f, " = "); |
399 |
|
instrument_expression(p->pn_expr, f); |
400 |
|
} |
401 |
|
break; |
402 |
|
case TOK_ASSIGN: |
403 |
|
case TOK_RB: |
404 |
|
case TOK_RC: |
405 |
|
/* destructuring */ |
406 |
|
instrument_expression(p, f); |
407 |
|
break; |
408 |
|
default: |
409 |
|
abort(); |
410 |
|
break; |
411 |
} |
} |
|
instrument_expression(p, f); |
|
412 |
} |
} |
|
Stream_write_char(f, ')'); |
|
413 |
} |
} |
414 |
|
|
415 |
/* |
/* |
583 |
assert(ATOM_IS_STRING(node->pn_atom)); |
assert(ATOM_IS_STRING(node->pn_atom)); |
584 |
{ |
{ |
585 |
JSString * s = ATOM_TO_STRING(node->pn_atom); |
JSString * s = ATOM_TO_STRING(node->pn_atom); |
586 |
bool is_keyword = (js_CheckKeyword(JSSTRING_CHARS(s), JSSTRING_LENGTH(s)) != TOK_EOF); |
bool must_quote; |
587 |
if (! is_keyword && js_IsIdentifier(s)) { |
if (JSSTRING_LENGTH(s) == 0) { |
588 |
Stream_write_char(f, '.'); |
must_quote = true; |
589 |
print_string_atom(node->pn_atom, f); |
} |
590 |
|
else if (js_CheckKeyword(JSSTRING_CHARS(s), JSSTRING_LENGTH(s)) != TOK_EOF) { |
591 |
|
must_quote = true; |
592 |
|
} |
593 |
|
else if (! js_IsIdentifier(s)) { |
594 |
|
must_quote = true; |
595 |
} |
} |
596 |
else { |
else { |
597 |
|
must_quote = false; |
598 |
|
} |
599 |
|
if (must_quote) { |
600 |
Stream_write_char(f, '['); |
Stream_write_char(f, '['); |
601 |
print_quoted_string_atom(node->pn_atom, f); |
print_quoted_string_atom(node->pn_atom, f); |
602 |
Stream_write_char(f, ']'); |
Stream_write_char(f, ']'); |
603 |
} |
} |
604 |
|
else { |
605 |
|
Stream_write_char(f, '.'); |
606 |
|
print_string_atom(node->pn_atom, f); |
607 |
|
} |
608 |
} |
} |
609 |
break; |
break; |
610 |
case TOK_LB: |
case TOK_LB: |
728 |
Stream_write_string(f, " in "); |
Stream_write_string(f, " in "); |
729 |
instrument_expression(node->pn_right, f); |
instrument_expression(node->pn_right, f); |
730 |
break; |
break; |
731 |
|
case TOK_LEXICALSCOPE: |
732 |
|
assert(node->pn_arity == PN_NAME); |
733 |
|
assert(node->pn_expr->pn_type == TOK_LET); |
734 |
|
assert(node->pn_expr->pn_arity == PN_BINARY); |
735 |
|
Stream_write_string(f, "let("); |
736 |
|
assert(node->pn_expr->pn_left->pn_type == TOK_LP); |
737 |
|
assert(node->pn_expr->pn_left->pn_arity == PN_LIST); |
738 |
|
instrument_declarations(node->pn_expr->pn_left, f); |
739 |
|
Stream_write_string(f, ") "); |
740 |
|
instrument_expression(node->pn_expr->pn_right, f); |
741 |
|
break; |
742 |
|
case TOK_YIELD: |
743 |
|
assert(node->pn_arity == PN_UNARY); |
744 |
|
Stream_write_string(f, "yield "); |
745 |
|
instrument_expression(node->pn_kid, f); |
746 |
|
break; |
747 |
|
case TOK_ARRAYCOMP: |
748 |
|
assert(node->pn_arity == PN_LIST); |
749 |
|
{ |
750 |
|
JSParseNode * block_node; |
751 |
|
switch (node->pn_count) { |
752 |
|
case 1: |
753 |
|
block_node = node->pn_head; |
754 |
|
break; |
755 |
|
case 2: |
756 |
|
block_node = node->pn_head->pn_next; |
757 |
|
break; |
758 |
|
default: |
759 |
|
abort(); |
760 |
|
break; |
761 |
|
} |
762 |
|
assert(block_node->pn_type == TOK_LEXICALSCOPE); |
763 |
|
assert(block_node->pn_arity == PN_NAME); |
764 |
|
JSParseNode * for_node = block_node->pn_expr; |
765 |
|
assert(for_node->pn_type == TOK_FOR); |
766 |
|
assert(for_node->pn_arity == PN_BINARY); |
767 |
|
JSParseNode * if_node = NULL; |
768 |
|
JSParseNode * push_node; |
769 |
|
switch (for_node->pn_right->pn_type) { |
770 |
|
case TOK_ARRAYPUSH: |
771 |
|
push_node = for_node->pn_right; |
772 |
|
assert(push_node->pn_arity == PN_UNARY); |
773 |
|
break; |
774 |
|
case TOK_IF: |
775 |
|
if_node = for_node->pn_right; |
776 |
|
assert(if_node->pn_arity == PN_TERNARY); |
777 |
|
push_node = if_node->pn_kid2; |
778 |
|
break; |
779 |
|
default: |
780 |
|
abort(); |
781 |
|
break; |
782 |
|
} |
783 |
|
Stream_write_char(f, '['); |
784 |
|
instrument_expression(push_node->pn_kid, f); |
785 |
|
Stream_write_char(f, ' '); |
786 |
|
output_for_in(for_node, f); |
787 |
|
if (if_node) { |
788 |
|
Stream_write_string(f, " if ("); |
789 |
|
instrument_expression(if_node->pn_kid1, f); |
790 |
|
Stream_write_char(f, ')'); |
791 |
|
} |
792 |
|
Stream_write_char(f, ']'); |
793 |
|
} |
794 |
|
break; |
795 |
|
case TOK_VAR: |
796 |
|
assert(node->pn_arity == PN_LIST); |
797 |
|
Stream_write_string(f, "var "); |
798 |
|
instrument_declarations(node, f); |
799 |
|
break; |
800 |
|
case TOK_LET: |
801 |
|
assert(node->pn_arity == PN_LIST); |
802 |
|
Stream_write_string(f, "let "); |
803 |
|
instrument_declarations(node, f); |
804 |
|
break; |
805 |
default: |
default: |
806 |
fatal("unsupported node type in file %s: %d", file_id, node->pn_type); |
fatal("unsupported node type in file %s: %d", file_id, node->pn_type); |
807 |
} |
} |
808 |
} |
} |
809 |
|
|
|
static void instrument_var_statement(JSParseNode * node, Stream * f, int indent) { |
|
|
assert(node->pn_arity == PN_LIST); |
|
|
Stream_printf(f, "%*s", indent, ""); |
|
|
Stream_write_string(f, "var "); |
|
|
for (struct JSParseNode * p = node->pn_u.list.head; p != NULL; p = p->pn_next) { |
|
|
assert(p->pn_type == TOK_NAME); |
|
|
assert(p->pn_arity == PN_NAME); |
|
|
if (p != node->pn_head) { |
|
|
Stream_write_string(f, ", "); |
|
|
} |
|
|
print_string_atom(p->pn_atom, f); |
|
|
if (p->pn_expr != NULL) { |
|
|
Stream_write_string(f, " = "); |
|
|
instrument_expression(p->pn_expr, f); |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
810 |
static void output_statement(JSParseNode * node, Stream * f, int indent, bool is_jscoverage_if) { |
static void output_statement(JSParseNode * node, Stream * f, int indent, bool is_jscoverage_if) { |
811 |
switch (node->pn_type) { |
switch (node->pn_type) { |
812 |
case TOK_FUNCTION: |
case TOK_FUNCTION: |
880 |
Stream_write_string(f, "switch ("); |
Stream_write_string(f, "switch ("); |
881 |
instrument_expression(node->pn_left, f); |
instrument_expression(node->pn_left, f); |
882 |
Stream_write_string(f, ") {\n"); |
Stream_write_string(f, ") {\n"); |
883 |
for (struct JSParseNode * p = node->pn_right->pn_head; p != NULL; p = p->pn_next) { |
{ |
884 |
Stream_printf(f, "%*s", indent, ""); |
JSParseNode * list = node->pn_right; |
885 |
switch (p->pn_type) { |
if (list->pn_type == TOK_LEXICALSCOPE) { |
886 |
case TOK_CASE: |
list = list->pn_expr; |
887 |
Stream_write_string(f, "case "); |
} |
888 |
instrument_expression(p->pn_left, f); |
for (struct JSParseNode * p = list->pn_head; p != NULL; p = p->pn_next) { |
889 |
Stream_write_string(f, ":\n"); |
Stream_printf(f, "%*s", indent, ""); |
890 |
break; |
switch (p->pn_type) { |
891 |
case TOK_DEFAULT: |
case TOK_CASE: |
892 |
Stream_write_string(f, "default:\n"); |
Stream_write_string(f, "case "); |
893 |
break; |
instrument_expression(p->pn_left, f); |
894 |
default: |
Stream_write_string(f, ":\n"); |
895 |
abort(); |
break; |
896 |
break; |
case TOK_DEFAULT: |
897 |
|
Stream_write_string(f, "default:\n"); |
898 |
|
break; |
899 |
|
default: |
900 |
|
abort(); |
901 |
|
break; |
902 |
|
} |
903 |
|
instrument_statement(p->pn_right, f, indent + 2, false); |
904 |
} |
} |
|
instrument_statement(p->pn_right, f, indent + 2, false); |
|
905 |
} |
} |
906 |
Stream_printf(f, "%*s", indent, ""); |
Stream_printf(f, "%*s", indent, ""); |
907 |
Stream_write_string(f, "}\n"); |
Stream_write_string(f, "}\n"); |
933 |
case TOK_FOR: |
case TOK_FOR: |
934 |
assert(node->pn_arity == PN_BINARY); |
assert(node->pn_arity == PN_BINARY); |
935 |
Stream_printf(f, "%*s", indent, ""); |
Stream_printf(f, "%*s", indent, ""); |
|
Stream_write_string(f, "for ("); |
|
936 |
switch (node->pn_left->pn_type) { |
switch (node->pn_left->pn_type) { |
937 |
case TOK_IN: |
case TOK_IN: |
938 |
/* for/in */ |
/* for/in */ |
939 |
assert(node->pn_left->pn_arity == PN_BINARY); |
assert(node->pn_left->pn_arity == PN_BINARY); |
940 |
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); |
|
941 |
break; |
break; |
942 |
case TOK_RESERVED: |
case TOK_RESERVED: |
943 |
/* for (;;) */ |
/* for (;;) */ |
944 |
assert(node->pn_left->pn_arity == PN_TERNARY); |
assert(node->pn_left->pn_arity == PN_TERNARY); |
945 |
|
Stream_write_string(f, "for ("); |
946 |
if (node->pn_left->pn_kid1) { |
if (node->pn_left->pn_kid1) { |
947 |
if (node->pn_left->pn_kid1->pn_type == TOK_VAR) { |
instrument_expression(node->pn_left->pn_kid1, f); |
|
instrument_var_statement(node->pn_left->pn_kid1, f, 0); |
|
|
} |
|
|
else { |
|
|
instrument_expression(node->pn_left->pn_kid1, f); |
|
|
} |
|
948 |
} |
} |
949 |
Stream_write_string(f, ";"); |
Stream_write_string(f, ";"); |
950 |
if (node->pn_left->pn_kid2) { |
if (node->pn_left->pn_kid2) { |
956 |
Stream_write_char(f, ' '); |
Stream_write_char(f, ' '); |
957 |
instrument_expression(node->pn_left->pn_kid3, f); |
instrument_expression(node->pn_left->pn_kid3, f); |
958 |
} |
} |
959 |
|
Stream_write_char(f, ')'); |
960 |
break; |
break; |
961 |
default: |
default: |
962 |
abort(); |
abort(); |
963 |
break; |
break; |
964 |
} |
} |
965 |
Stream_write_string(f, ") {\n"); |
Stream_write_string(f, " {\n"); |
966 |
instrument_statement(node->pn_right, f, indent + 2, false); |
instrument_statement(node->pn_right, f, indent + 2, false); |
967 |
Stream_write_string(f, "}\n"); |
Stream_write_string(f, "}\n"); |
968 |
break; |
break; |
987 |
assert(catch->pn_type == TOK_CATCH); |
assert(catch->pn_type == TOK_CATCH); |
988 |
Stream_printf(f, "%*s", indent, ""); |
Stream_printf(f, "%*s", indent, ""); |
989 |
Stream_write_string(f, "catch ("); |
Stream_write_string(f, "catch ("); |
990 |
|
/* this may not be a name - destructuring assignment */ |
991 |
|
/* |
992 |
assert(catch->pn_kid1->pn_arity == PN_NAME); |
assert(catch->pn_kid1->pn_arity == PN_NAME); |
993 |
print_string_atom(catch->pn_kid1->pn_atom, f); |
print_string_atom(catch->pn_kid1->pn_atom, f); |
994 |
|
*/ |
995 |
|
instrument_expression(catch->pn_kid1, f); |
996 |
if (catch->pn_kid2) { |
if (catch->pn_kid2) { |
997 |
Stream_write_string(f, " if "); |
Stream_write_string(f, " if "); |
998 |
instrument_expression(catch->pn_kid2, f); |
instrument_expression(catch->pn_kid2, f); |
1037 |
Stream_write_string(f, "}\n"); |
Stream_write_string(f, "}\n"); |
1038 |
break; |
break; |
1039 |
case TOK_VAR: |
case TOK_VAR: |
1040 |
instrument_var_statement(node, f, indent); |
Stream_printf(f, "%*s", indent, ""); |
1041 |
|
instrument_expression(node, f); |
1042 |
Stream_write_string(f, ";\n"); |
Stream_write_string(f, ";\n"); |
1043 |
break; |
break; |
1044 |
case TOK_RETURN: |
case TOK_RETURN: |
1073 |
*/ |
*/ |
1074 |
output_statement(node->pn_expr, f, indent, false); |
output_statement(node->pn_expr, f, indent, false); |
1075 |
break; |
break; |
1076 |
|
case TOK_LEXICALSCOPE: |
1077 |
|
/* let statement */ |
1078 |
|
assert(node->pn_arity == PN_NAME); |
1079 |
|
switch (node->pn_expr->pn_type) { |
1080 |
|
case TOK_LET: |
1081 |
|
/* let statement */ |
1082 |
|
assert(node->pn_expr->pn_arity == PN_BINARY); |
1083 |
|
instrument_statement(node->pn_expr, f, indent, false); |
1084 |
|
break; |
1085 |
|
case TOK_LC: |
1086 |
|
/* block */ |
1087 |
|
Stream_printf(f, "%*s", indent, ""); |
1088 |
|
Stream_write_string(f, "{\n"); |
1089 |
|
instrument_statement(node->pn_expr, f, indent + 2, false); |
1090 |
|
Stream_printf(f, "%*s", indent, ""); |
1091 |
|
Stream_write_string(f, "}\n"); |
1092 |
|
break; |
1093 |
|
case TOK_FOR: |
1094 |
|
instrument_statement(node->pn_expr, f, indent, false); |
1095 |
|
break; |
1096 |
|
default: |
1097 |
|
abort(); |
1098 |
|
break; |
1099 |
|
} |
1100 |
|
break; |
1101 |
|
case TOK_LET: |
1102 |
|
switch (node->pn_arity) { |
1103 |
|
case PN_BINARY: |
1104 |
|
/* let statement */ |
1105 |
|
Stream_printf(f, "%*s", indent, ""); |
1106 |
|
Stream_write_string(f, "let ("); |
1107 |
|
assert(node->pn_left->pn_type == TOK_LP); |
1108 |
|
assert(node->pn_left->pn_arity == PN_LIST); |
1109 |
|
instrument_declarations(node->pn_left, f); |
1110 |
|
Stream_write_string(f, ") {\n"); |
1111 |
|
instrument_statement(node->pn_right, f, indent + 2, false); |
1112 |
|
Stream_printf(f, "%*s", indent, ""); |
1113 |
|
Stream_write_string(f, "}\n"); |
1114 |
|
break; |
1115 |
|
case PN_LIST: |
1116 |
|
/* let definition */ |
1117 |
|
Stream_printf(f, "%*s", indent, ""); |
1118 |
|
instrument_expression(node, f); |
1119 |
|
Stream_write_string(f, ";\n"); |
1120 |
|
break; |
1121 |
|
default: |
1122 |
|
abort(); |
1123 |
|
break; |
1124 |
|
} |
1125 |
|
break; |
1126 |
|
case TOK_DEBUGGER: |
1127 |
|
Stream_printf(f, "%*s", indent, ""); |
1128 |
|
Stream_write_string(f, "debugger;\n"); |
1129 |
|
break; |
1130 |
default: |
default: |
1131 |
fatal("unsupported node type in file %s: %d", file_id, node->pn_type); |
fatal("unsupported node type in file %s: %d", file_id, node->pn_type); |
1132 |
} |
} |
1138 |
TOK_EXPORT, TOK_IMPORT are not handled. |
TOK_EXPORT, TOK_IMPORT are not handled. |
1139 |
*/ |
*/ |
1140 |
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) { |
1141 |
if (node->pn_type != TOK_LC) { |
if (node->pn_type != TOK_LC && node->pn_type != TOK_LEXICALSCOPE) { |
1142 |
uint16_t line = node->pn_pos.begin.lineno; |
uint16_t line = node->pn_pos.begin.lineno; |
1143 |
if (line > num_lines) { |
if (line > num_lines) { |
1144 |
fatal("%s: script contains more than 65,535 lines", file_id); |
fatal("%s: script contains more than 65,535 lines", file_id); |