28 |
#include <jsapi.h> |
#include <jsapi.h> |
29 |
#include <jsarena.h> |
#include <jsarena.h> |
30 |
#include <jsatom.h> |
#include <jsatom.h> |
31 |
|
#include <jsemit.h> |
32 |
#include <jsexn.h> |
#include <jsexn.h> |
33 |
#include <jsfun.h> |
#include <jsfun.h> |
34 |
#include <jsinterp.h> |
#include <jsinterp.h> |
258 |
|
|
259 |
static void instrument_expression(JSParseNode * node, Stream * f); |
static void instrument_expression(JSParseNode * node, Stream * f); |
260 |
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); |
261 |
|
static void output_statement(JSParseNode * node, Stream * f, int indent, bool is_jscoverage_if); |
262 |
|
|
263 |
enum FunctionType { |
enum FunctionType { |
264 |
FUNCTION_NORMAL, |
FUNCTION_NORMAL, |
265 |
FUNCTION_GETTER_OR_SETTER |
FUNCTION_GETTER_OR_SETTER |
266 |
}; |
}; |
267 |
|
|
268 |
|
static void output_for_in(JSParseNode * node, Stream * f) { |
269 |
|
assert(node->pn_type == TOK_FOR); |
270 |
|
assert(node->pn_arity == PN_BINARY); |
271 |
|
Stream_write_string(f, "for "); |
272 |
|
if (node->pn_iflags & JSITER_FOREACH) { |
273 |
|
Stream_write_string(f, "each "); |
274 |
|
} |
275 |
|
Stream_write_char(f, '('); |
276 |
|
instrument_expression(node->pn_left, f); |
277 |
|
Stream_write_char(f, ')'); |
278 |
|
} |
279 |
|
|
280 |
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) { |
281 |
assert(node->pn_type == TOK_FUNCTION); |
assert(node->pn_type == TOK_FUNCTION); |
282 |
assert(node->pn_arity == PN_FUNC); |
assert(node->pn_arity == PN_FUNC); |
315 |
Stream_write_string(f, ", "); |
Stream_write_string(f, ", "); |
316 |
} |
} |
317 |
JSAtom * param = JS_LOCAL_NAME_TO_ATOM(local_names[i]); |
JSAtom * param = JS_LOCAL_NAME_TO_ATOM(local_names[i]); |
318 |
|
if (param == NULL) { |
319 |
|
fatal("unsupported parameter type for function: %s", file_id); |
320 |
|
} |
321 |
print_string_atom(param, f); |
print_string_atom(param, f); |
322 |
} |
} |
323 |
JS_FinishArenaPool(&pool); |
JS_FinishArenaPool(&pool); |
324 |
Stream_write_string(f, ") {\n"); |
Stream_write_string(f, ") {\n"); |
325 |
|
|
326 |
/* function body */ |
/* function body */ |
327 |
instrument_statement(node->pn_body, f, indent + 2, false); |
if (function->flags & JSFUN_EXPR_CLOSURE) { |
328 |
|
/* expression closure */ |
329 |
|
output_statement(node->pn_body, f, indent + 2, false); |
330 |
|
} |
331 |
|
else { |
332 |
|
instrument_statement(node->pn_body, f, indent + 2, false); |
333 |
|
} |
334 |
|
|
335 |
Stream_write_string(f, "}\n"); |
Stream_write_string(f, "}\n"); |
336 |
} |
} |
337 |
|
|
338 |
static void instrument_function_call(JSParseNode * node, Stream * f) { |
static void instrument_function_call(JSParseNode * node, Stream * f) { |
339 |
instrument_expression(node->pn_head, f); |
JSParseNode * function_node = node->pn_head; |
340 |
|
if (function_node->pn_type == TOK_FUNCTION) { |
341 |
|
JSObject * object = function_node->pn_funpob->object; |
342 |
|
assert(JS_ObjectIsFunction(context, object)); |
343 |
|
JSFunction * function = (JSFunction *) JS_GetPrivate(context, object); |
344 |
|
assert(function); |
345 |
|
assert(object == &function->object); |
346 |
|
|
347 |
|
if (function_node->pn_flags & TCF_GENEXP_LAMBDA) { |
348 |
|
/* it's a generator expression */ |
349 |
|
JSParseNode * lexical_scope_node = function_node->pn_body; |
350 |
|
assert(lexical_scope_node->pn_type == TOK_LEXICALSCOPE); |
351 |
|
assert(lexical_scope_node->pn_arity == PN_NAME); |
352 |
|
JSParseNode * for_node = lexical_scope_node->pn_body; |
353 |
|
assert(for_node->pn_type == TOK_FOR); |
354 |
|
assert(for_node->pn_arity == PN_BINARY); |
355 |
|
JSParseNode * if_node = NULL; |
356 |
|
JSParseNode * semi_node; |
357 |
|
switch (for_node->pn_right->pn_type) { |
358 |
|
case TOK_SEMI: |
359 |
|
semi_node = for_node->pn_right; |
360 |
|
break; |
361 |
|
case TOK_IF: |
362 |
|
if_node = for_node->pn_right; |
363 |
|
assert(if_node->pn_arity == PN_TERNARY); |
364 |
|
semi_node = if_node->pn_kid2; |
365 |
|
assert(semi_node->pn_type == TOK_SEMI); |
366 |
|
break; |
367 |
|
default: |
368 |
|
abort(); |
369 |
|
break; |
370 |
|
} |
371 |
|
assert(semi_node->pn_arity == PN_UNARY); |
372 |
|
JSParseNode * yield_node = semi_node->pn_kid; |
373 |
|
assert(yield_node->pn_type == TOK_YIELD); |
374 |
|
Stream_write_char(f, '('); |
375 |
|
instrument_expression(yield_node->pn_kid, f); |
376 |
|
Stream_write_char(f, ' '); |
377 |
|
output_for_in(for_node, f); |
378 |
|
if (if_node) { |
379 |
|
Stream_write_string(f, " if ("); |
380 |
|
instrument_expression(if_node->pn_kid1, f); |
381 |
|
Stream_write_char(f, ')'); |
382 |
|
} |
383 |
|
Stream_write_char(f, ')'); |
384 |
|
return; |
385 |
|
} |
386 |
|
else { |
387 |
|
Stream_write_char(f, '('); |
388 |
|
instrument_expression(function_node, f); |
389 |
|
Stream_write_char(f, ')'); |
390 |
|
} |
391 |
|
} |
392 |
|
else { |
393 |
|
instrument_expression(function_node, f); |
394 |
|
} |
395 |
Stream_write_char(f, '('); |
Stream_write_char(f, '('); |
396 |
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) { |
397 |
if (p != node->pn_head->pn_next) { |
if (p != node->pn_head->pn_next) { |
398 |
Stream_write_string(f, ", "); |
Stream_write_string(f, ", "); |
399 |
} |
} |
601 |
assert(ATOM_IS_STRING(node->pn_atom)); |
assert(ATOM_IS_STRING(node->pn_atom)); |
602 |
{ |
{ |
603 |
JSString * s = ATOM_TO_STRING(node->pn_atom); |
JSString * s = ATOM_TO_STRING(node->pn_atom); |
604 |
bool is_keyword = (js_CheckKeyword(JSSTRING_CHARS(s), JSSTRING_LENGTH(s)) != TOK_EOF); |
bool must_quote; |
605 |
if (! is_keyword && js_IsIdentifier(s)) { |
if (JSSTRING_LENGTH(s) == 0) { |
606 |
Stream_write_char(f, '.'); |
must_quote = true; |
607 |
print_string_atom(node->pn_atom, f); |
} |
608 |
|
else if (js_CheckKeyword(JSSTRING_CHARS(s), JSSTRING_LENGTH(s)) != TOK_EOF) { |
609 |
|
must_quote = true; |
610 |
|
} |
611 |
|
else if (! js_IsIdentifier(s)) { |
612 |
|
must_quote = true; |
613 |
} |
} |
614 |
else { |
else { |
615 |
|
must_quote = false; |
616 |
|
} |
617 |
|
if (must_quote) { |
618 |
Stream_write_char(f, '['); |
Stream_write_char(f, '['); |
619 |
print_quoted_string_atom(node->pn_atom, f); |
print_quoted_string_atom(node->pn_atom, f); |
620 |
Stream_write_char(f, ']'); |
Stream_write_char(f, ']'); |
621 |
} |
} |
622 |
|
else { |
623 |
|
Stream_write_char(f, '.'); |
624 |
|
print_string_atom(node->pn_atom, f); |
625 |
|
} |
626 |
} |
} |
627 |
break; |
break; |
628 |
case TOK_LB: |
case TOK_LB: |
762 |
Stream_write_string(f, "yield "); |
Stream_write_string(f, "yield "); |
763 |
instrument_expression(node->pn_kid, f); |
instrument_expression(node->pn_kid, f); |
764 |
break; |
break; |
765 |
|
case TOK_ARRAYCOMP: |
766 |
|
assert(node->pn_arity == PN_LIST); |
767 |
|
{ |
768 |
|
JSParseNode * block_node; |
769 |
|
switch (node->pn_count) { |
770 |
|
case 1: |
771 |
|
block_node = node->pn_head; |
772 |
|
break; |
773 |
|
case 2: |
774 |
|
block_node = node->pn_head->pn_next; |
775 |
|
break; |
776 |
|
default: |
777 |
|
abort(); |
778 |
|
break; |
779 |
|
} |
780 |
|
assert(block_node->pn_type == TOK_LEXICALSCOPE); |
781 |
|
assert(block_node->pn_arity == PN_NAME); |
782 |
|
JSParseNode * for_node = block_node->pn_expr; |
783 |
|
assert(for_node->pn_type == TOK_FOR); |
784 |
|
assert(for_node->pn_arity == PN_BINARY); |
785 |
|
JSParseNode * if_node = NULL; |
786 |
|
JSParseNode * push_node; |
787 |
|
switch (for_node->pn_right->pn_type) { |
788 |
|
case TOK_ARRAYPUSH: |
789 |
|
push_node = for_node->pn_right; |
790 |
|
assert(push_node->pn_arity == PN_UNARY); |
791 |
|
break; |
792 |
|
case TOK_IF: |
793 |
|
if_node = for_node->pn_right; |
794 |
|
assert(if_node->pn_arity == PN_TERNARY); |
795 |
|
push_node = if_node->pn_kid2; |
796 |
|
break; |
797 |
|
default: |
798 |
|
abort(); |
799 |
|
break; |
800 |
|
} |
801 |
|
Stream_write_char(f, '['); |
802 |
|
instrument_expression(push_node->pn_kid, f); |
803 |
|
Stream_write_char(f, ' '); |
804 |
|
output_for_in(for_node, f); |
805 |
|
if (if_node) { |
806 |
|
Stream_write_string(f, " if ("); |
807 |
|
instrument_expression(if_node->pn_kid1, f); |
808 |
|
Stream_write_char(f, ')'); |
809 |
|
} |
810 |
|
Stream_write_char(f, ']'); |
811 |
|
} |
812 |
|
break; |
813 |
|
case TOK_VAR: |
814 |
|
assert(node->pn_arity == PN_LIST); |
815 |
|
Stream_write_string(f, "var "); |
816 |
|
instrument_declarations(node, f); |
817 |
|
break; |
818 |
|
case TOK_LET: |
819 |
|
assert(node->pn_arity == PN_LIST); |
820 |
|
Stream_write_string(f, "let "); |
821 |
|
instrument_declarations(node, f); |
822 |
|
break; |
823 |
default: |
default: |
824 |
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); |
825 |
} |
} |
826 |
} |
} |
827 |
|
|
|
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 "); |
|
|
instrument_declarations(node, f); |
|
|
} |
|
|
|
|
|
static void instrument_let_definition(JSParseNode * node, Stream * f, int indent) { |
|
|
assert(node->pn_arity == PN_LIST); |
|
|
Stream_printf(f, "%*s", indent, ""); |
|
|
Stream_write_string(f, "let "); |
|
|
instrument_declarations(node, f); |
|
|
} |
|
|
|
|
828 |
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) { |
829 |
switch (node->pn_type) { |
switch (node->pn_type) { |
830 |
case TOK_FUNCTION: |
case TOK_FUNCTION: |
898 |
Stream_write_string(f, "switch ("); |
Stream_write_string(f, "switch ("); |
899 |
instrument_expression(node->pn_left, f); |
instrument_expression(node->pn_left, f); |
900 |
Stream_write_string(f, ") {\n"); |
Stream_write_string(f, ") {\n"); |
901 |
for (struct JSParseNode * p = node->pn_right->pn_head; p != NULL; p = p->pn_next) { |
{ |
902 |
Stream_printf(f, "%*s", indent, ""); |
JSParseNode * list = node->pn_right; |
903 |
switch (p->pn_type) { |
if (list->pn_type == TOK_LEXICALSCOPE) { |
904 |
case TOK_CASE: |
list = list->pn_expr; |
905 |
Stream_write_string(f, "case "); |
} |
906 |
instrument_expression(p->pn_left, f); |
for (struct JSParseNode * p = list->pn_head; p != NULL; p = p->pn_next) { |
907 |
Stream_write_string(f, ":\n"); |
Stream_printf(f, "%*s", indent, ""); |
908 |
break; |
switch (p->pn_type) { |
909 |
case TOK_DEFAULT: |
case TOK_CASE: |
910 |
Stream_write_string(f, "default:\n"); |
Stream_write_string(f, "case "); |
911 |
break; |
instrument_expression(p->pn_left, f); |
912 |
default: |
Stream_write_string(f, ":\n"); |
913 |
abort(); |
break; |
914 |
break; |
case TOK_DEFAULT: |
915 |
|
Stream_write_string(f, "default:\n"); |
916 |
|
break; |
917 |
|
default: |
918 |
|
abort(); |
919 |
|
break; |
920 |
|
} |
921 |
|
instrument_statement(p->pn_right, f, indent + 2, false); |
922 |
} |
} |
|
instrument_statement(p->pn_right, f, indent + 2, false); |
|
923 |
} |
} |
924 |
Stream_printf(f, "%*s", indent, ""); |
Stream_printf(f, "%*s", indent, ""); |
925 |
Stream_write_string(f, "}\n"); |
Stream_write_string(f, "}\n"); |
951 |
case TOK_FOR: |
case TOK_FOR: |
952 |
assert(node->pn_arity == PN_BINARY); |
assert(node->pn_arity == PN_BINARY); |
953 |
Stream_printf(f, "%*s", indent, ""); |
Stream_printf(f, "%*s", indent, ""); |
|
Stream_write_string(f, "for "); |
|
|
if (node->pn_iflags & JSITER_FOREACH) { |
|
|
Stream_write_string(f, "each "); |
|
|
} |
|
|
Stream_write_char(f, '('); |
|
954 |
switch (node->pn_left->pn_type) { |
switch (node->pn_left->pn_type) { |
955 |
case TOK_IN: |
case TOK_IN: |
956 |
/* for/in */ |
/* for/in */ |
957 |
assert(node->pn_left->pn_arity == PN_BINARY); |
assert(node->pn_left->pn_arity == PN_BINARY); |
958 |
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_LET: |
|
|
instrument_let_definition(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); |
|
959 |
break; |
break; |
960 |
case TOK_RESERVED: |
case TOK_RESERVED: |
961 |
/* for (;;) */ |
/* for (;;) */ |
962 |
assert(node->pn_left->pn_arity == PN_TERNARY); |
assert(node->pn_left->pn_arity == PN_TERNARY); |
963 |
|
Stream_write_string(f, "for ("); |
964 |
if (node->pn_left->pn_kid1) { |
if (node->pn_left->pn_kid1) { |
965 |
switch (node->pn_left->pn_kid1->pn_type) { |
instrument_expression(node->pn_left->pn_kid1, f); |
|
case TOK_VAR: |
|
|
instrument_var_statement(node->pn_left->pn_kid1, f, 0); |
|
|
break; |
|
|
case TOK_LET: |
|
|
instrument_let_definition(node->pn_left->pn_kid1, f, 0); |
|
|
break; |
|
|
default: |
|
|
instrument_expression(node->pn_left->pn_kid1, f); |
|
|
break; |
|
|
} |
|
966 |
} |
} |
967 |
Stream_write_string(f, ";"); |
Stream_write_string(f, ";"); |
968 |
if (node->pn_left->pn_kid2) { |
if (node->pn_left->pn_kid2) { |
974 |
Stream_write_char(f, ' '); |
Stream_write_char(f, ' '); |
975 |
instrument_expression(node->pn_left->pn_kid3, f); |
instrument_expression(node->pn_left->pn_kid3, f); |
976 |
} |
} |
977 |
|
Stream_write_char(f, ')'); |
978 |
break; |
break; |
979 |
default: |
default: |
980 |
abort(); |
abort(); |
981 |
break; |
break; |
982 |
} |
} |
983 |
Stream_write_string(f, ") {\n"); |
Stream_write_string(f, " {\n"); |
984 |
instrument_statement(node->pn_right, f, indent + 2, false); |
instrument_statement(node->pn_right, f, indent + 2, false); |
985 |
Stream_write_string(f, "}\n"); |
Stream_write_string(f, "}\n"); |
986 |
break; |
break; |
1055 |
Stream_write_string(f, "}\n"); |
Stream_write_string(f, "}\n"); |
1056 |
break; |
break; |
1057 |
case TOK_VAR: |
case TOK_VAR: |
1058 |
instrument_var_statement(node, f, indent); |
Stream_printf(f, "%*s", indent, ""); |
1059 |
|
instrument_expression(node, f); |
1060 |
Stream_write_string(f, ";\n"); |
Stream_write_string(f, ";\n"); |
1061 |
break; |
break; |
1062 |
case TOK_RETURN: |
case TOK_RETURN: |
1132 |
break; |
break; |
1133 |
case PN_LIST: |
case PN_LIST: |
1134 |
/* let definition */ |
/* let definition */ |
1135 |
instrument_let_definition(node, f, indent); |
Stream_printf(f, "%*s", indent, ""); |
1136 |
|
instrument_expression(node, f); |
1137 |
Stream_write_string(f, ";\n"); |
Stream_write_string(f, ";\n"); |
1138 |
break; |
break; |
1139 |
default: |
default: |
1141 |
break; |
break; |
1142 |
} |
} |
1143 |
break; |
break; |
1144 |
|
case TOK_DEBUGGER: |
1145 |
|
Stream_printf(f, "%*s", indent, ""); |
1146 |
|
Stream_write_string(f, "debugger;\n"); |
1147 |
|
break; |
1148 |
default: |
default: |
1149 |
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); |
1150 |
} |
} |