148 |
static void instrument_expression(JSParseNode * node, Stream * f); |
static void instrument_expression(JSParseNode * node, Stream * f); |
149 |
static void instrument_statement(JSParseNode * node, Stream * f, int indent); |
static void instrument_statement(JSParseNode * node, Stream * f, int indent); |
150 |
|
|
151 |
static void instrument_function(JSParseNode * node, Stream * f, int indent) { |
enum FunctionType { |
152 |
assert(node->pn_arity == PN_FUNC); |
FUNCTION_NORMAL, |
153 |
assert(ATOM_IS_OBJECT(node->pn_funAtom)); |
FUNCTION_GETTER_OR_SETTER |
154 |
JSObject * object = ATOM_TO_OBJECT(node->pn_funAtom); |
}; |
155 |
assert(JS_ObjectIsFunction(context, object)); |
|
156 |
JSFunction * function = (JSFunction *) JS_GetPrivate(context, object); |
static void instrument_function(JSParseNode * node, Stream * f, int indent, enum FunctionType type) { |
157 |
assert(function); |
assert(node->pn_arity == PN_FUNC); |
158 |
assert(object == function->object); |
assert(ATOM_IS_OBJECT(node->pn_funAtom)); |
159 |
Stream_printf(f, "%*s", indent, ""); |
JSObject * object = ATOM_TO_OBJECT(node->pn_funAtom); |
160 |
|
assert(JS_ObjectIsFunction(context, object)); |
161 |
|
JSFunction * function = (JSFunction *) JS_GetPrivate(context, object); |
162 |
|
assert(function); |
163 |
|
assert(object == function->object); |
164 |
|
Stream_printf(f, "%*s", indent, ""); |
165 |
|
if (type == FUNCTION_NORMAL) { |
166 |
Stream_write_string(f, "function"); |
Stream_write_string(f, "function"); |
167 |
|
} |
168 |
|
|
169 |
/* function name */ |
/* function name */ |
170 |
if (function->atom) { |
if (function->atom) { |
171 |
Stream_write_char(f, ' '); |
Stream_write_char(f, ' '); |
172 |
print_string_atom(function->atom, f); |
print_string_atom(function->atom, f); |
173 |
} |
} |
174 |
|
|
175 |
/* function parameters */ |
/* function parameters */ |
176 |
Stream_write_string(f, "("); |
Stream_write_string(f, "("); |
177 |
JSAtom ** params = xnew(JSAtom *, function->nargs); |
JSAtom ** params = xnew(JSAtom *, function->nargs); |
178 |
for (int i = 0; i < function->nargs; i++) { |
for (int i = 0; i < function->nargs; i++) { |
179 |
/* initialize to NULL for sanity check */ |
/* initialize to NULL for sanity check */ |
180 |
params[i] = NULL; |
params[i] = NULL; |
181 |
} |
} |
182 |
JSScope * scope = OBJ_SCOPE(object); |
JSScope * scope = OBJ_SCOPE(object); |
183 |
for (JSScopeProperty * scope_property = SCOPE_LAST_PROP(scope); scope_property != NULL; scope_property = scope_property->parent) { |
for (JSScopeProperty * scope_property = SCOPE_LAST_PROP(scope); scope_property != NULL; scope_property = scope_property->parent) { |
184 |
if (scope_property->getter != js_GetArgument) { |
if (scope_property->getter != js_GetArgument) { |
185 |
continue; |
continue; |
186 |
} |
} |
187 |
assert(scope_property->flags & SPROP_HAS_SHORTID); |
assert(scope_property->flags & SPROP_HAS_SHORTID); |
188 |
assert((uint16) scope_property->shortid < function->nargs); |
assert((uint16) scope_property->shortid < function->nargs); |
189 |
assert(JSID_IS_ATOM(scope_property->id)); |
assert(JSID_IS_ATOM(scope_property->id)); |
190 |
params[(uint16) scope_property->shortid] = JSID_TO_ATOM(scope_property->id); |
params[(uint16) scope_property->shortid] = JSID_TO_ATOM(scope_property->id); |
191 |
} |
} |
192 |
for (int i = 0; i < function->nargs; i++) { |
for (int i = 0; i < function->nargs; i++) { |
193 |
assert(params[i] != NULL); |
assert(params[i] != NULL); |
194 |
if (i > 0) { |
if (i > 0) { |
195 |
Stream_write_string(f, ", "); |
Stream_write_string(f, ", "); |
|
} |
|
|
if (ATOM_IS_STRING(params[i])) { |
|
|
print_string_atom(params[i], f); |
|
|
} |
|
196 |
} |
} |
197 |
Stream_write_string(f, ") {\n"); |
if (ATOM_IS_STRING(params[i])) { |
198 |
free(params); |
print_string_atom(params[i], f); |
199 |
|
} |
200 |
|
} |
201 |
|
Stream_write_string(f, ") {\n"); |
202 |
|
free(params); |
203 |
|
|
204 |
/* function body */ |
/* function body */ |
205 |
instrument_statement(node->pn_body, f, indent + 2); |
instrument_statement(node->pn_body, f, indent + 2); |
206 |
|
|
207 |
Stream_write_string(f, "}\n"); |
Stream_write_string(f, "}\n"); |
208 |
} |
} |
209 |
|
|
210 |
static void instrument_function_call(JSParseNode * node, Stream * f) { |
static void instrument_function_call(JSParseNode * node, Stream * f) { |
235 |
static void instrument_expression(JSParseNode * node, Stream * f) { |
static void instrument_expression(JSParseNode * node, Stream * f) { |
236 |
switch (node->pn_type) { |
switch (node->pn_type) { |
237 |
case TOK_FUNCTION: |
case TOK_FUNCTION: |
238 |
instrument_function(node, f, 0); |
instrument_function(node, f, 0, FUNCTION_NORMAL); |
239 |
break; |
break; |
240 |
case TOK_COMMA: |
case TOK_COMMA: |
241 |
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) { |
442 |
if (p != node->pn_head) { |
if (p != node->pn_head) { |
443 |
Stream_write_string(f, ", "); |
Stream_write_string(f, ", "); |
444 |
} |
} |
445 |
instrument_expression(p->pn_left, f); |
|
446 |
Stream_write_string(f, ": "); |
/* check whether this is a getter or setter */ |
447 |
instrument_expression(p->pn_right, f); |
switch (p->pn_op) { |
448 |
|
case JSOP_GETTER: |
449 |
|
Stream_write_string(f, "get "); |
450 |
|
instrument_expression(p->pn_left, f); |
451 |
|
instrument_function(p->pn_right, f, 0, FUNCTION_GETTER_OR_SETTER); |
452 |
|
break; |
453 |
|
case JSOP_SETTER: |
454 |
|
Stream_write_string(f, "set "); |
455 |
|
instrument_expression(p->pn_left, f); |
456 |
|
instrument_function(p->pn_right, f, 0, FUNCTION_GETTER_OR_SETTER); |
457 |
|
break; |
458 |
|
default: |
459 |
|
instrument_expression(p->pn_left, f); |
460 |
|
Stream_write_string(f, ": "); |
461 |
|
instrument_expression(p->pn_right, f); |
462 |
|
break; |
463 |
|
} |
464 |
} |
} |
465 |
Stream_write_char(f, '}'); |
Stream_write_char(f, '}'); |
466 |
break; |
break; |
564 |
static void output_statement(JSParseNode * node, Stream * f, int indent) { |
static void output_statement(JSParseNode * node, Stream * f, int indent) { |
565 |
switch (node->pn_type) { |
switch (node->pn_type) { |
566 |
case TOK_FUNCTION: |
case TOK_FUNCTION: |
567 |
instrument_function(node, f, indent); |
instrument_function(node, f, indent, FUNCTION_NORMAL); |
568 |
break; |
break; |
569 |
case TOK_LC: |
case TOK_LC: |
570 |
assert(node->pn_arity == PN_LIST); |
assert(node->pn_arity == PN_LIST); |
881 |
Stream_write(output, instrumented->data, instrumented->length); |
Stream_write(output, instrumented->data, instrumented->length); |
882 |
Stream_write_char(output, '\n'); |
Stream_write_char(output, '\n'); |
883 |
|
|
884 |
/* copy the original source to the output */ |
/* conditionals */ |
885 |
|
bool has_conditionals = false; |
886 |
|
size_t line_number = 0; |
887 |
size_t i = 0; |
size_t i = 0; |
888 |
while (i < input_length) { |
while (i < input_length) { |
889 |
|
line_number++; |
890 |
|
size_t line_start = i; |
891 |
|
while (i < input_length && base[i] != '\r' && base[i] != '\n') { |
892 |
|
i++; |
893 |
|
} |
894 |
|
size_t line_end = i; |
895 |
|
if (i < input_length) { |
896 |
|
if (base[i] == '\r') { |
897 |
|
line_end = i; |
898 |
|
i++; |
899 |
|
if (i < input_length && base[i] == '\n') { |
900 |
|
i++; |
901 |
|
} |
902 |
|
} |
903 |
|
else if (base[i] == '\n') { |
904 |
|
line_end = i; |
905 |
|
i++; |
906 |
|
} |
907 |
|
else { |
908 |
|
abort(); |
909 |
|
} |
910 |
|
} |
911 |
|
char * line = js_DeflateString(context, base + line_start, line_end - line_start); |
912 |
|
if (str_starts_with(line, "//#JSCOVERAGE_IF")) { |
913 |
|
if (! has_conditionals) { |
914 |
|
has_conditionals = true; |
915 |
|
Stream_printf(output, "_$jscoverage['%s'].conditionals = [];\n", file_id); |
916 |
|
} |
917 |
|
Stream_printf(output, "if (!%s) {\n", line + 16); |
918 |
|
Stream_printf(output, " _$jscoverage['%s'].conditionals[%d] = ", file_id, line_number); |
919 |
|
} |
920 |
|
else if (str_starts_with(line, "//#JSCOVERAGE_ENDIF")) { |
921 |
|
Stream_printf(output, "%d;\n", line_number); |
922 |
|
Stream_printf(output, "}\n"); |
923 |
|
} |
924 |
|
JS_free(context, line); |
925 |
|
} |
926 |
|
|
927 |
|
/* copy the original source to the output */ |
928 |
|
i = 0; |
929 |
|
while (i < input_length) { |
930 |
Stream_write_string(output, "// "); |
Stream_write_string(output, "// "); |
931 |
size_t line_start = i; |
size_t line_start = i; |
932 |
while (i < input_length && base[i] != '\r' && base[i] != '\n') { |
while (i < input_length && base[i] != '\r' && base[i] != '\n') { |
968 |
copy_resource("jscoverage.html", destination_directory); |
copy_resource("jscoverage.html", destination_directory); |
969 |
copy_resource("jscoverage.css", destination_directory); |
copy_resource("jscoverage.css", destination_directory); |
970 |
copy_resource("jscoverage.js", destination_directory); |
copy_resource("jscoverage.js", destination_directory); |
971 |
|
copy_resource("jscoverage-ie.js", destination_directory); |
972 |
copy_resource("jscoverage-throbber.gif", destination_directory); |
copy_resource("jscoverage-throbber.gif", destination_directory); |
973 |
copy_resource("jscoverage-sh_main.js", destination_directory); |
copy_resource("jscoverage-sh_main.js", destination_directory); |
974 |
copy_resource("jscoverage-sh_javascript.js", destination_directory); |
copy_resource("jscoverage-sh_javascript.js", destination_directory); |