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 |
305 |
assert(if_node->pn_arity == PN_TERNARY); |
assert(if_node->pn_arity == PN_TERNARY); |
306 |
p = if_node->pn_kid2; |
p = if_node->pn_kid2; |
307 |
} |
} |
308 |
assert(p->pn_arity == PN_UNARY); |
switch (p->pn_arity) { |
309 |
p = p->pn_kid; |
case PN_UNARY: |
|
if (p->pn_type == TOK_YIELD) { |
|
|
/* for generator expressions */ |
|
310 |
p = p->pn_kid; |
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 |
|
|
|
output_expression(p, f, false); |
|
332 |
p = for_node; |
p = for_node; |
333 |
while (p->pn_type == TOK_FOR) { |
while (p->pn_type == TOK_FOR) { |
334 |
Stream_write_char(f, ' '); |
Stream_write_char(f, ' '); |
335 |
output_for_in(p, f); |
output_for_in(p, f); |
336 |
p = p->pn_right; |
p = p->pn_right; |
337 |
} |
} |
338 |
if (if_node) { |
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 ("); |
Stream_write_string(f, " if ("); |
344 |
output_expression(if_node->pn_kid1, f, false); |
output_expression(if_node->pn_kid1, f, false); |
345 |
Stream_write_char(f, ')'); |
Stream_write_char(f, ')'); |
387 |
if (param == NULL) { |
if (param == NULL) { |
388 |
destructuring = true; |
destructuring = true; |
389 |
JSParseNode * expression = NULL; |
JSParseNode * expression = NULL; |
390 |
assert(node->pn_body->pn_type == TOK_LC || node->pn_body->pn_type == TOK_BODY); |
assert(node->pn_body->pn_type == TOK_LC || node->pn_body->pn_type == TOK_SEQ); |
391 |
JSParseNode * semi = node->pn_body->pn_head; |
JSParseNode * semi = node->pn_body->pn_head; |
392 |
assert(semi->pn_type == TOK_SEMI); |
assert(semi->pn_type == TOK_SEMI); |
393 |
JSParseNode * comma = semi->pn_kid; |
JSParseNode * comma = semi->pn_kid; |
414 |
/* function body */ |
/* function body */ |
415 |
if (function->flags & JSFUN_EXPR_CLOSURE) { |
if (function->flags & JSFUN_EXPR_CLOSURE) { |
416 |
/* expression closure - use output_statement instead of instrument_statement */ |
/* expression closure - use output_statement instead of instrument_statement */ |
417 |
if (node->pn_body->pn_type == TOK_BODY) { |
if (node->pn_body->pn_type == TOK_SEQ) { |
418 |
assert(node->pn_body->pn_arity == PN_LIST); |
assert(node->pn_body->pn_arity == PN_LIST); |
419 |
assert(node->pn_body->pn_count == 2); |
assert(node->pn_body->pn_count == 2); |
420 |
output_statement(node->pn_body->pn_head->pn_next, f, indent + 2, false); |
output_statement(node->pn_body->pn_head->pn_next, f, indent + 2, false); |
735 |
Stream_write_string(f, "set "); |
Stream_write_string(f, "set "); |
736 |
} |
} |
737 |
output_expression(p->pn_left, f, false); |
output_expression(p->pn_left, f, false); |
738 |
|
Stream_write_char(f, ' '); |
739 |
if (p->pn_right->pn_type != TOK_FUNCTION) { |
if (p->pn_right->pn_type != TOK_FUNCTION) { |
740 |
fatal_source(file_id, p->pn_pos.begin.lineno, "expected function"); |
fatal_source(file_id, p->pn_pos.begin.lineno, "expected function"); |
741 |
} |
} |
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) { |
else if (node->pn_dval == INFINITY) { |
796 |
Stream_write_string(f, "Number.POSITIVE_INFINITY"); |
Stream_write_string(f, "Number.POSITIVE_INFINITY"); |
798 |
else if (node->pn_dval == -INFINITY) { |
else if (node->pn_dval == -INFINITY) { |
799 |
Stream_write_string(f, "Number.NEGATIVE_INFINITY"); |
Stream_write_string(f, "Number.NEGATIVE_INFINITY"); |
800 |
} |
} |
801 |
else if (node->pn_dval == NAN) { |
else if (isnan(node->pn_dval)) { |
802 |
Stream_write_string(f, "Number.NaN"); |
Stream_write_string(f, "Number.NaN"); |
803 |
} |
} |
804 |
else { |
else { |
1021 |
assert(node->pn_left->pn_arity == PN_BINARY); |
assert(node->pn_left->pn_arity == PN_BINARY); |
1022 |
output_for_in(node, f); |
output_for_in(node, 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 ("); |
Stream_write_string(f, "for ("); |
1065 |
assert(node->pn_kid2->pn_type == TOK_RESERVED); |
assert(node->pn_kid2->pn_type == TOK_RESERVED); |
1066 |
for (JSParseNode * scope = node->pn_kid2->pn_head; scope != NULL; scope = scope->pn_next) { |
for (JSParseNode * scope = node->pn_kid2->pn_head; scope != NULL; scope = scope->pn_next) { |
1067 |
assert(scope->pn_type == TOK_LEXICALSCOPE); |
assert(scope->pn_type == TOK_LEXICALSCOPE); |
1068 |
JSParseNode * catch = scope->pn_expr; |
JSParseNode * catch_node = scope->pn_expr; |
1069 |
assert(catch->pn_type == TOK_CATCH); |
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 |
output_expression(catch->pn_kid1, f, false); |
output_expression(catch_node->pn_kid1, f, false); |
1073 |
if (catch->pn_kid2) { |
if (catch_node->pn_kid2) { |
1074 |
Stream_write_string(f, " if "); |
Stream_write_string(f, " if "); |
1075 |
output_expression(catch->pn_kid2, f, false); |
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 |
} |
} |
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 |
} |
} |
1217 |
Stream_printf(f, "%*s", indent, ""); |
Stream_printf(f, "%*s", indent, ""); |
1218 |
Stream_write_string(f, "debugger;\n"); |
Stream_write_string(f, "debugger;\n"); |
1219 |
break; |
break; |
1220 |
|
case TOK_SEQ: |
1221 |
|
/* |
1222 |
|
This occurs with the statement: |
1223 |
|
for (var a = b in c) {} |
1224 |
|
*/ |
1225 |
|
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; |
1230 |
default: |
default: |
1231 |
fatal_source(file_id, node->pn_pos.begin.lineno, "unsupported node type (%d)", node->pn_type); |
fatal_source(file_id, node->pn_pos.begin.lineno, "unsupported node type (%d)", node->pn_type); |
1232 |
} |
} |
1308 |
} |
} |
1309 |
JS_SetErrorReporter(context, old_error_reporter); |
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 |
} |
} |
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"); |
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 |
|
|
1694 |
} |
} |
1695 |
JSParseNode * root = js_ParseScript(context, global, &parse_context); |
JSParseNode * root = js_ParseScript(context, global, &parse_context); |
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 |
result = -1; |
result = -1; |
1703 |
goto done; |
goto done; |
1708 |
result = -1; |
result = -1; |
1709 |
goto done; |
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 |
result = -1; |
result = -1; |
1716 |
goto done; |
goto done; |
1717 |
} |
} |
1718 |
JSParseNode * parenthesized = semi->pn_kid; |
object = semi->pn_kid; |
|
|
|
|
/* this must be a parenthesized expression */ |
|
|
if (parenthesized->pn_type != TOK_RP) { |
|
|
result = -1; |
|
|
goto done; |
|
|
} |
|
|
JSParseNode * object = parenthesized->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) { |
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); |
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; |