Skip to content

Commit

Permalink
More work on call stack and trace
Browse files Browse the repository at this point in the history
- Makes sure that all usages of `fh_call` are properly set up and push a
  new state onto the call stack. This needs some cleaning up yet.
  `fh_call` should manage the stack push and pop internally, taking
  whatever call metadata it needs as argument.
- Errors that occur in the execution of included files now properly
  display the script name.
- TODO: Errors while executing externally defined functions (in main or
  other) do not yet display the correct script name.
- Fixes a memory issue with the Function#apply implementation when the
  length of the argument array is 0.
  • Loading branch information
ndreynolds committed Apr 16, 2013
1 parent 227e713 commit 240541e
Show file tree
Hide file tree
Showing 9 changed files with 72 additions and 25 deletions.
4 changes: 2 additions & 2 deletions src/eval.c
Original file line number Diff line number Diff line change
Expand Up @@ -706,7 +706,7 @@ setup_call_env(js_val *ctx, js_val *this, js_val *func, js_args *args)
fh_set(scope, func_node->sval, func);

// Set up the (array-like) arguments object.
int i, arglen = ARGLEN(args);
unsigned i, arglen = ARGLEN(args);
for (i = 0; i < arglen; i++)
fh_set(arguments, JSNUMKEY(i)->string.ptr, ARG(args, i));
fh_set_class(arguments, "Arguments");
Expand Down Expand Up @@ -832,7 +832,7 @@ new_exp(js_val *ctx, ast_node *exp)
args = fh_new_args(0, 0, 0);
}

eval_state *state= fh_new_state(exp->line, exp->column);
eval_state *state = fh_new_state(exp->line, exp->column);
fh_push_state(state);
state->construct = true;

Expand Down
25 changes: 16 additions & 9 deletions src/flathead.c
Original file line number Diff line number Diff line change
Expand Up @@ -198,27 +198,30 @@ fh_new_prop(js_prop_flags flags)
void
fh_pop_state()
{
if (fh->statetrace) {
eval_state *pop = fh->statetrace;
fh->statetrace = pop->parent;
if (fh->callstack) {
eval_state *pop = fh->callstack;
fh->callstack = pop->parent;
free(pop);
}
}

void
fh_push_state(eval_state *state)
{
state->parent = fh->statetrace;
fh->statetrace = state;
state->parent = fh->callstack;
fh->callstack = state;
}

eval_state *
fh_new_state(int line, int column)
{
eval_state *state = malloc(sizeof(eval_state));
memset(state, 0, sizeof(eval_state));

state->line = line;
state->column = column;
state->caller_info = NULL;
state->script_name = fh->script_name;

state->ctx = NULL;
state->this = NULL;
Expand Down Expand Up @@ -486,14 +489,18 @@ fh_error(eval_state *state, js_error_type type, const char *tpl, ...)
fprintf(stderr, "\n");

while (state != NULL) {
fprintf(stderr, " at %s:%u:%u\n",
fh->script_name, state->line, state->column);
if (state->caller_info)
fprintf(stderr, " at %s (%s:%u:%u)\n",
state->caller_info, state->script_name, state->line, state->column);
else
fprintf(stderr, " at %s:%u:%u\n",
state->script_name, state->line, state->column);
if (!state->parent) break;
state = state->parent;
}

if (fh->opt_interactive) {
fh->statetrace = NULL;
fh->callstack = NULL;
longjmp(fh->jmpbuf, 1);
}
exit(1);
Expand Down Expand Up @@ -580,7 +587,7 @@ fh_arg_len(js_args *args)

// Although arrays can be up to 2^32 - 1 in length, we limit
// the number of arguments to 2^16 - 1 (UINT_MAX).
unsigned int i = 0;
unsigned i = 0;
while (i < UINT_MAX)
{
if (args->arg != NULL) i++;
Expand Down
10 changes: 5 additions & 5 deletions src/flathead.h
Original file line number Diff line number Diff line change
Expand Up @@ -149,11 +149,9 @@ typedef struct {
bool opt_keep_history_file;
const char *opt_history_filename;

const char *script_name;

jmp_buf jmpbuf; // used to handle errors within REPL

struct eval_state *statetrace;
char *script_name;
struct eval_state *callstack;

struct js_val *function_proto; // cache prototype pointers
struct js_val *object_proto;
Expand All @@ -167,6 +165,8 @@ typedef struct eval_state {
bool construct;
struct js_val *ctx;
struct js_val *this;
char *caller_info;
char *script_name;
struct eval_state *parent;
} eval_state;

Expand Down Expand Up @@ -194,7 +194,7 @@ typedef struct {
} js_number;

typedef struct {
long length;
unsigned long length;
char *ptr;
} js_string;

Expand Down
10 changes: 8 additions & 2 deletions src/grammar.y
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@
#define NEW_WITH(exp,stmt) NEW_NODE(NODE_WITH_STMT,exp,stmt,0,0,0)

void yyerror(const char *);
int yycolumn;
int yydebug;
int fh_get_input(char *, int);
ast_node *root;
Expand Down Expand Up @@ -1193,6 +1194,10 @@ fh_eval_string(char *string, js_val *ctx)
bool tmp = fh->opt_interactive;
fh->opt_interactive = false;

eval_state *prior_state = fh_new_state(yylloc.first_line, yylloc.last_line);
yycolumn = 0;
yylineno = 0;

YY_BUFFER_STATE buffer = yy_scan_string(string);
yyparse();

Expand All @@ -1202,6 +1207,8 @@ fh_eval_string(char *string, js_val *ctx)
js_val *res = fh_eval(ctx, root);
yy_delete_buffer(buffer);
fh->opt_interactive = tmp;
yylineno = prior_state->line;
yycolumn = prior_state->column;
return res;
}

Expand Down Expand Up @@ -1261,9 +1268,8 @@ main(int argc, char **argv)
DEBUG(fh_eval_file(source, fh->global));
}
}
else {
else
fh_eval_file(source, fh->global);
}

return 0;
}
26 changes: 24 additions & 2 deletions src/runtime/lib/Array.c
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,10 @@ cmp(js_prop *a, js_prop *b)
static int
cmp_js(js_prop *a, js_prop *b)
{
eval_state *state = js_cmp_state;
js_args *args = fh_new_args(a->ptr, b->ptr, 0);
js_val *result = fh_call(state->ctx, JSUNDEF(), state, js_cmp_func, args);
eval_state *call_state = fh_new_state(js_cmp_state->line, js_cmp_state->column);
fh_push_state(call_state);
js_val *result = fh_call(js_cmp_state->ctx, JSUNDEF(), call_state, js_cmp_func, args);
return TO_BOOL(result)->number.val > 0;
}

Expand Down Expand Up @@ -565,11 +566,14 @@ arr_proto_filter(js_val *instance, js_args *args, eval_state *state)

js_val *ikey, *jkey, *val, *result;
js_args *cbargs;
eval_state *call_state;
unsigned long i = 0, j = 0;
for (; i < len; i++) {
ikey = JSNUMKEY(i);
val = fh_get(instance, ikey->string.ptr);
cbargs = fh_new_args(val, JSNUM(i), instance);
call_state = fh_new_state(state->line, state->column);
fh_push_state(call_state);
result = fh_call(state->ctx, this, state, callback, cbargs);
if (TO_BOOL(result)->boolean.val) {
jkey = JSNUMKEY(j++);
Expand All @@ -591,11 +595,14 @@ arr_proto_for_each(js_val *instance, js_args *args, eval_state *state)

js_val *key, *val;
js_args *cbargs;
eval_state *call_state;
unsigned long i;
for (i = 0; i < len; i++) {
key = JSNUMKEY(i);
val = fh_get(instance, key->string.ptr);
cbargs = fh_new_args(val, JSNUM(i), instance);
call_state = fh_new_state(state->line, state->column);
fh_push_state(call_state);
fh_call(state->ctx, this, state, callback, cbargs);
}

Expand All @@ -612,11 +619,14 @@ arr_proto_every(js_val *instance, js_args *args, eval_state *state)

js_val *key, *val, *result;
js_args *cbargs;
eval_state *call_state;
unsigned long i;
for (i = 0; i < len; i++) {
key = JSNUMKEY(i);
val = fh_get(instance, key->string.ptr);
cbargs = fh_new_args(val, JSNUM(i), instance);
call_state = fh_new_state(state->line, state->column);
fh_push_state(call_state);
result = fh_call(state->ctx, this, state, callback, cbargs);
if (!TO_BOOL(result)->boolean.val)
return JSBOOL(0);
Expand All @@ -636,11 +646,14 @@ arr_proto_map(js_val *instance, js_args *args, eval_state *state)

js_val *key, *val, *result;
js_args *cbargs;
eval_state *call_state;
unsigned long i;
for (i = 0; i < len; i++) {
key = JSNUMKEY(i);
val = fh_get(instance, key->string.ptr);
cbargs = fh_new_args(val, JSNUM(i), instance);
call_state = fh_new_state(state->line, state->column);
fh_push_state(call_state);
result = fh_call(state->ctx, this, state, callback, cbargs);
fh_set(map, key->string.ptr, result);
}
Expand All @@ -659,11 +672,14 @@ arr_proto_some(js_val *instance, js_args *args, eval_state *state)

js_val *key, *val, *result;
js_args *cbargs;
eval_state *call_state;
unsigned long i;
for (i = 0; i < len; i++) {
key = JSNUMKEY(i);
val = fh_get(instance, key->string.ptr);
cbargs = fh_new_args(val, JSNUM(i), instance);
call_state = fh_new_state(state->line, state->column);
fh_push_state(call_state);
result = fh_call(state->ctx, this, state, callback, cbargs);
if (TO_BOOL(result)->boolean.val)
return JSBOOL(1);
Expand Down Expand Up @@ -693,10 +709,13 @@ arr_proto_reduce(js_val *instance, js_args *args, eval_state *state)

js_val *key, *val;
js_args *cbargs;
eval_state *call_state;
for (; i < len; i++) {
key = JSNUMKEY(i);
val = fh_get(instance, key->string.ptr);
cbargs = fh_new_args(reduction, val, JSNUM(i));
call_state = fh_new_state(state->line, state->column);
fh_push_state(call_state);
reduction = fh_call(state->ctx, JSUNDEF(), state, callback, cbargs);
}

Expand Down Expand Up @@ -733,9 +752,12 @@ arr_proto_reduce_right(js_val *instance, js_args *args, eval_state *state)

js_val *val;
js_args *cbargs;
eval_state *call_state;
do {
val = fh_get(instance, JSNUMKEY(i)->string.ptr);
cbargs = fh_new_args(reduction, val, JSNUM(i));
call_state = fh_new_state(state->line, state->column);
fh_push_state(call_state);
reduction = fh_call(state->ctx, JSUNDEF(), state, callback, cbargs);
} while (i--);

Expand Down
14 changes: 11 additions & 3 deletions src/runtime/lib/Function.c
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,14 @@ func_proto_apply(js_val *instance, js_args *args, eval_state *state)
{
js_val *this = ARG(args, 0);
js_val *arr = ARG(args, 1);

js_args *func_args = malloc(sizeof(js_args));
js_args *func_args_head = func_args;
func_args->arg = NULL;
func_args->next = NULL;

unsigned len = arr->object.length;
unsigned i;
unsigned long len = arr->object.length;
unsigned long i;

for (i = 0; i < len; i++) {
func_args->arg = fh_get(arr, JSNUMKEY(i)->string.ptr);
Expand All @@ -61,7 +64,9 @@ func_proto_apply(js_val *instance, js_args *args, eval_state *state)
}
}

return fh_call(state->ctx, this, state, instance, func_args_head);
eval_state *call_state = fh_new_state(state->line, state->column);
fh_push_state(call_state);
return fh_call(state->ctx, this, call_state, instance, func_args_head);
}

// Function.prototype.apply(thisValue[, arg1[, arg2[, ...]]])
Expand Down Expand Up @@ -89,6 +94,9 @@ func_proto_call(js_val *instance, js_args *args, eval_state *state)
args->arg = NULL;
if (args->next)
args = args->next;

eval_state *call_state = fh_new_state(state->line, state->column);
fh_push_state(call_state);
return fh_call(state->ctx, this, state, instance, args);
}

Expand Down
3 changes: 2 additions & 1 deletion src/runtime/lib/String.c
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,9 @@ js_val *
str_proto_char_at(js_val *instance, js_args *args, eval_state *state)
{
int index = TO_NUM(ARG(args, 0))->number.val;
int len = instance->string.length;

if (index < 0 || index >= instance->string.length)
if (index < 0 || index >= len)
return JSSTR("");
char *str = malloc(2);
sprintf(str, "%c", instance->string.ptr[index]);
Expand Down
3 changes: 3 additions & 0 deletions src/runtime/runtime.c
Original file line number Diff line number Diff line change
Expand Up @@ -170,9 +170,12 @@ load_file(char *name)
if (result != size)
return -1;

char *prev_script = fh->script_name;
fh->script_name = name;
fcontent[size] = '\0';
fh_eval_string(fcontent, fh->global);
free(fcontent);
fh->script_name = prev_script;
return 0;
}

Expand Down
2 changes: 1 addition & 1 deletion test/test_function_global.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ test('Function#apply(thisValue[, argsArray])', function() {
assertEquals(21, fib.apply(null, [8]));
assertEquals(21, fib.apply(undefined, [8]));
assertEquals(24, getX.apply(thisValue, []));
assertEquals(99, getY.call(thisValue));
assertEquals(99, getY.apply(thisValue));
assertThis.apply(this, [this]);
assertThis.apply(thisValue, [thisValue]);
});
Expand Down

0 comments on commit 240541e

Please sign in to comment.